Blender V5.0
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
8
9#include <cmath>
10#include <cstdlib>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_node_types.h"
16#include "DNA_object_types.h"
17#include "DNA_scene_types.h"
18#include "DNA_space_types.h"
19
20#include "BLI_hash.h"
21#include "BLI_heap.h"
22#include "BLI_kdopbvh.hh"
23#include "BLI_kdtree.h"
24#include "BLI_lasso_2d.hh"
25#include "BLI_listbase.h"
26#include "BLI_math_geom.h"
27#include "BLI_math_matrix.h"
28#include "BLI_math_vector.h"
29#include "BLI_memarena.h"
30#include "BLI_polyfill_2d.h"
32#include "BLI_utildefines.h"
33#include "BLI_vector_list.hh"
34
35#include "BLT_translation.hh"
36
37#include "BKE_context.hh"
38#include "BKE_customdata.hh"
39#include "BKE_editmesh.hh"
40#include "BKE_layer.hh"
41#include "BKE_material.hh"
42#include "BKE_mesh.hh"
43#include "BKE_mesh_mapping.hh"
44#include "BKE_report.hh"
45
46#include "DEG_depsgraph.hh"
48
49#include "ED_image.hh"
50#include "ED_mesh.hh"
51#include "ED_screen.hh"
52#include "ED_select_utils.hh"
53#include "ED_uvedit.hh"
54
55#include "RNA_access.hh"
56#include "RNA_define.hh"
57#include "RNA_enum_types.hh"
58
59#include "WM_api.hh"
60#include "WM_types.hh"
61
62#include "UI_view2d.hh"
63
64#include "uvedit_intern.hh"
65
66using blender::Array;
67using blender::int2;
68using blender::Span;
69using blender::Vector;
70
71static void uv_select_all_perform_multi_ex(const Scene *scene,
72 Span<Object *> objects,
73 int action,
74 const Object *ob_exclude);
75static void uv_select_all_perform_multi(const Scene *scene, Span<Object *> objects, int action);
76
77static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select);
78static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select);
79static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMesh *bm);
80
81static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
82 const ToolSettings *ts,
83 Object *obedit);
84
97
98/* -------------------------------------------------------------------- */
104
113
115{
116 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
117 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) && bm->uv_select_sync_valid) {
118 if (ese && ese->htype == BM_VERT) {
119 BMVert *v = (BMVert *)ese->ele;
120
121 BMLoop *l;
122 BMIter liter;
123
124 /* Prioritize face, edge then vert selection.
125 * This may be overkill, even so, be deterministic and favor loops connected to selection. */
126 BMLoop *l_select_vert = nullptr;
127 BMLoop *l_select_edge = nullptr;
128 BMLoop *l_select_edge_pair = nullptr;
129 BMLoop *l_select_face = nullptr;
130
131 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
133 continue;
134 }
136 const bool select_edge_prev = BM_loop_edge_uvselect_test(l->prev);
137 const bool select_edge_next = BM_loop_edge_uvselect_test(l);
138 const bool select_face = BM_elem_flag_test(l->f, BM_ELEM_SELECT_UV);
139 l_select_vert = l;
140 if (select_edge_prev || select_edge_next) {
141 l_select_edge_pair = l;
142 }
143 if (select_edge_prev && select_edge_next) {
144 l_select_edge_pair = l;
145 }
146 if (select_face) {
147 l_select_face = l;
148 }
149 }
150 }
151 if (l_select_face) {
152 return l_select_face;
153 }
154 if (l_select_edge_pair) {
155 return l_select_edge_pair;
156 }
157 if (l_select_edge) {
158 return l_select_edge;
159 }
160 return l_select_vert;
161 }
162 return nullptr;
163 }
164
165 if (ese && ese->prev) {
166 BMEditSelection *ese_prev = ese->prev;
167 if ((ese->htype == BM_VERT) && (ese_prev->htype == BM_FACE)) {
168 /* May be null. */
169 return BM_face_vert_share_loop((BMFace *)ese_prev->ele, (BMVert *)ese->ele);
170 }
171 }
172 return nullptr;
173}
174
183
185{
186 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
187 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) && bm->uv_select_sync_valid) {
188 if (ese && ese->htype == BM_EDGE) {
189 BMEdge *e = (BMEdge *)ese->ele;
190
191 BMLoop *l;
192 BMIter liter;
193
194 /* Prioritize face then edge selection.
195 * This may be overkill, even so, be deterministic and favor loops connected to selection. */
196 BMLoop *l_select_vert = nullptr;
197 BMLoop *l_select_face = nullptr;
198
199 BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
201 continue;
202 }
204 const bool select_face = BM_elem_flag_test(l->f, BM_ELEM_SELECT_UV);
205 l_select_vert = l;
206 if (select_face) {
207 l_select_face = l;
208 }
209 }
210 }
211
212 if (l_select_face) {
213 return l_select_face;
214 }
215 return l_select_vert;
216 }
217 return nullptr;
218 }
219
220 if (ese && ese->prev) {
221 BMEditSelection *ese_prev = ese->prev;
222 if ((ese->htype == BM_EDGE) && (ese_prev->htype == BM_FACE)) {
223 /* May be null. */
224 return BM_face_edge_share_loop((BMFace *)ese_prev->ele, (BMEdge *)ese->ele);
225 }
226 }
227 return nullptr;
228}
229
231
232/* -------------------------------------------------------------------- */
235
237{
239 if (ts->uv_sticky == UV_STICKY_VERT) {
240 /* In this case use the original mesh selection. */
241 return true;
242 }
243 return false;
244}
245
247{
248 return bm->uv_select_sync_valid || ED_uvedit_sync_uvselect_ignore(ts);
249}
250
252{
253 BLI_assert(!bm->uv_select_sync_valid);
254
255 /* Otherwise, ensure UV select is up to date. */
256 switch (ts->uv_sticky) {
257 case UV_STICKY_LOCATION: {
258 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
260 break;
261 }
262 case UV_STICKY_DISABLE: {
264 break;
265 }
266 case UV_STICKY_VERT: {
268 break;
269 }
270 }
271}
272
274{
275 /* Select sync wont be needed when mode switching. */
277 bm->uv_select_sync_valid = false;
278 return;
279 }
280
281 /* In most cases the caller will ensure this,
282 * check here to allow for this to be called outside of the UV editor. */
283 if (!CustomData_has_layer(&bm->ldata, CD_PROP_FLOAT2)) {
284 bm->uv_select_sync_valid = false;
285 return;
286 }
287
288 /* Select sync already calculated. */
289 if (bm->uv_select_sync_valid) {
290 return;
291 }
292
294}
295
297{
298 const ToolSettings *ts = scene->toolsettings;
299 char uv_selectmode = UV_SELECT_VERT;
300
301 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
302 if (ts->selectmode & SCE_SELECT_VERTEX) {
303 uv_selectmode = UV_SELECT_VERT;
304 }
305 else if (ts->selectmode & SCE_SELECT_EDGE) {
306 uv_selectmode = UV_SELECT_EDGE;
307 }
308 else if (ts->selectmode & SCE_SELECT_FACE) {
309 uv_selectmode = UV_SELECT_FACE;
310 }
311 }
312 else {
313 if (ts->uv_selectmode & UV_SELECT_VERT) {
314 uv_selectmode = UV_SELECT_VERT;
315 }
316 else if (ts->uv_selectmode & UV_SELECT_EDGE) {
317 uv_selectmode = UV_SELECT_EDGE;
318 }
319 else if (ts->uv_selectmode & UV_SELECT_FACE) {
320 uv_selectmode = UV_SELECT_FACE;
321 }
322 }
323 return uv_selectmode;
324}
325
327{
328 if ((ts->uv_flag & UV_FLAG_SELECT_ISLAND) == 0) {
329 return false;
330 }
331
332 /* NOTE: when "strict" only return true when it's possible to select an island in isolation.
333 * At the moment none of the callers require this however it may be necessary to ignore the
334 * "island" selection option for some operations in the future.
335 * This could be exposed as an argument. */
336 const bool strict = false;
337
338 if (strict) {
339 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
341 return false;
342 }
343 }
344 }
345
346 return true;
347}
348
350{
351 /* bmesh API handles flushing but not on de-select */
352 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
353 if (bm->uv_select_sync_valid) {
355 if (ts->uv_sticky == UV_STICKY_LOCATION) {
356 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
358 }
360 }
361 else {
362 if (ts->selectmode != SCE_SELECT_FACE) {
363 if (select == false) {
365 }
366 else {
367 if (ts->selectmode & SCE_SELECT_VERTEX) {
369 }
370 else {
371 /* Use instead of #BM_mesh_select_flush so selecting edges doesn't
372 * flush vertex to face selection, see: #117320. */
374 }
375 }
376 }
377 }
378
379 if (select == false) {
381 }
382 }
383}
384
385static void uvedit_vertex_select_tagged(BMesh *bm, const Scene *scene, bool select)
386{
387 BMFace *efa;
388 BMLoop *l;
389 BMIter iter, liter;
390
391 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
392 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
395 }
396 }
397 }
398}
399
401{
402 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
403 return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0);
404 }
406}
407bool uvedit_face_visible_test(const Scene *scene, const BMFace *efa)
408{
409 return uvedit_face_visible_test_ex(scene->toolsettings, efa);
410}
411
412bool uvedit_face_select_test_ex(const ToolSettings *ts, const BMesh *bm, const BMFace *efa)
413{
414 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
415 if (bm->uv_select_sync_valid == false || ED_uvedit_sync_uvselect_ignore(ts)) {
417 }
418 /* Caller checks for visibility. */
421 }
422
423 if (ts->uv_selectmode == UV_SELECT_FACE) {
425 return false;
426 }
427 return true;
428 }
429 const char hflag_test = (ts->uv_selectmode & UV_SELECT_VERT) ? BM_ELEM_SELECT_UV :
431 const BMLoop *l_first = BM_FACE_FIRST_LOOP(efa);
432 const BMLoop *l_iter = l_first;
433 do {
434 if (!BM_elem_flag_test(l_iter, hflag_test)) {
435 return false;
436 }
437 } while ((l_iter = l_iter->next) != l_first);
438 return true;
439}
440bool uvedit_face_select_test(const Scene *scene, const BMesh *bm, const BMFace *efa)
441{
442 return uvedit_face_select_test_ex(scene->toolsettings, bm, efa);
443}
444
446 const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const BMUVOffsets &offsets)
447{
448 const ToolSettings *ts = scene->toolsettings;
449 const char sticky = ts->uv_sticky;
450 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
452 uvedit_face_select_set(scene, bm, efa, select);
453 return;
454 }
456 }
457 if (!uvedit_face_visible_test(scene, efa)) {
458 return;
459 }
460 /* NOTE: Previously face selections done in sticky vertex mode selected stray UV vertices
461 * (not part of any face selections). This now uses the sticky location mode logic instead. */
462 switch (sticky) {
463 case UV_STICKY_DISABLE: {
464 uvedit_face_select_set(scene, bm, efa, select);
465 break;
466 }
467 default: {
468 /* UV_STICKY_LOCATION and UV_STICKY_VERT modes. */
469 uvedit_face_select_shared_vert(scene, bm, efa, select, offsets);
470 }
471 }
472}
473
475 BMesh *bm,
476 BMFace *efa,
477 const bool select,
478
479 const BMUVOffsets &offsets)
480{
481 const ToolSettings *ts = scene->toolsettings;
482 BMLoop *l;
483 BMIter liter;
484
485 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
486 if (ts->uv_sticky == UV_STICKY_VERT) {
488 return;
489 }
491
492 /* NOTE: the logic is different enough to split out,
493 * mainly because it's possible to de-select a face but have all it's edges selected.
494 *
495 * NOTE: An alternative to this function would be to simply set the face selection
496 * and flush the entire mesh afterwards, mentioning this because the checks here are
497 * fairly involved. */
498
499 if (ts->uv_sticky == UV_STICKY_DISABLE) {
501 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
504 }
505 }
506 else if (ts->uv_sticky == UV_STICKY_LOCATION) {
508 if (select) {
509 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
510 BM_loop_vert_uvselect_set_shared(bm, l, true, offsets.uv);
511 BM_loop_edge_uvselect_set_shared(bm, l, true, offsets.uv);
512 }
513 }
514 else {
515 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
517 BM_loop_vert_uvselect_set_shared(bm, l, false, offsets.uv);
518 }
520 BM_loop_edge_uvselect_set_shared(bm, l, false, offsets.uv);
521 }
522 }
523 }
524 }
525 return;
526 }
527
529 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
531
532 if (select) {
534 }
535 else {
536 if (!uvedit_vert_is_face_select_any_other(ts, bm, l, offsets)) {
538 }
539 }
540 }
541}
542
543void uvedit_face_select_set(const Scene *scene, BMesh *bm, BMFace *efa, const bool select)
544{
545 if (select) {
546 uvedit_face_select_enable(scene, bm, efa);
547 }
548 else {
549 uvedit_face_select_disable(scene, bm, efa);
550 }
551}
552
554{
555 const ToolSettings *ts = scene->toolsettings;
556
557 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
559 BM_face_select_set(bm, efa, true);
560 }
561 else {
562 BM_face_uvselect_set(bm, efa, true);
563 }
564 }
565 else {
566 BMLoop *l;
567 BMIter liter;
568
569 uvedit_face_select_set_no_sync(ts, bm, efa, true);
570 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
573 }
574 }
575}
576
578{
579 const ToolSettings *ts = scene->toolsettings;
580
581 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
583 BM_face_select_set(bm, efa, false);
584 }
585 else {
586 BM_face_uvselect_set(bm, efa, false);
587 }
588 }
589 else {
590 BMLoop *l;
591 BMIter liter;
592
593 uvedit_face_select_set_no_sync(ts, bm, efa, false);
594 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
597 }
598 }
599}
600
602 const BMesh *bm,
603 const BMLoop *l,
604 const BMUVOffsets &offsets)
605{
606 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
607 if ((bm->uv_select_sync_valid == false) && (ts->selectmode == SCE_SELECT_FACE)) {
608 /* Face only is a special case that can respect sticky modes. */
609 switch (ts->uv_sticky) {
610 case UV_STICKY_LOCATION: {
612 return true;
613 }
614 if (uvedit_edge_is_face_select_any_other(ts, bm, l, offsets)) {
615 return true;
616 }
617 return false;
618 }
619 case UV_STICKY_DISABLE: {
621 }
622 default: {
623 /* #UV_STICKY_VERT */
625 }
626 }
628 }
629
630 if (bm->uv_select_sync_valid == false || ED_uvedit_sync_uvselect_ignore(ts)) {
631 if (ts->selectmode & SCE_SELECT_FACE) {
633 }
636 }
637 return BM_elem_flag_test(l->v, BM_ELEM_SELECT) &&
639 }
640
642 }
643
644 if (ts->uv_selectmode & UV_SELECT_VERT) {
645 return uvedit_vert_select_get_no_sync(ts, bm, l) &&
647 }
649}
650
652 const BMesh *bm,
653 const BMLoop *l,
654 const BMUVOffsets &offsets)
655{
656 return uvedit_edge_select_test_ex(scene->toolsettings, bm, l, offsets);
657}
658
660 BMesh *bm,
661 BMLoop *l,
662 const bool select,
663
664 const BMUVOffsets &offsets)
665{
666 const ToolSettings *ts = scene->toolsettings;
667 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
670 return;
671 }
673 }
674
675 const int sticky = ts->uv_sticky;
676 switch (sticky) {
677 case UV_STICKY_DISABLE: {
678 if (uvedit_face_visible_test(scene, l->f)) {
680 }
681 break;
682 }
683 case UV_STICKY_VERT: {
685 break;
686 }
687 default: {
688 /* UV_STICKY_LOCATION (Fallback) */
690 break;
691 }
692 }
693}
694
696 BMesh *bm,
697 BMLoop *l)
698{
699 const ToolSettings *ts = scene->toolsettings;
700 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
701 if (bm->uv_select_sync_valid == false || ED_uvedit_sync_uvselect_ignore(ts)) {
702 /* Use mesh selection. */
704 }
705 /* Caller checks for visibility. */
708 }
710}
711
713{
714 const ToolSettings *ts = scene->toolsettings;
715 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
716 if (bm->uv_select_sync_valid == false || ED_uvedit_sync_uvselect_ignore(ts)) {
717 /* Use mesh selection. */
719 }
720 /* Caller checks for visibility. */
723 }
725}
726
728 BMesh *bm,
729 BMLoop *l,
730 const bool select,
731 const int sticky_flag,
732 const BMUVOffsets &offsets)
733{
735 /* Set edge flags. Rely on this for face visibility checks */
736 uvedit_edge_select_set_noflush(scene, bm, l, select, sticky_flag, offsets);
737
738 /* Vert selections. */
739 BMLoop *l_iter = l;
740 do {
741 if (select) {
742 if (bm_loop_select_edge_check_internal(scene, bm, l_iter)) {
743 uvedit_uv_select_shared_vert(scene, bm, l_iter, true, UV_STICKY_LOCATION, offsets);
744 uvedit_uv_select_shared_vert(scene, bm, l_iter->next, true, UV_STICKY_LOCATION, offsets);
745 }
746 }
747 else {
748 if (!bm_loop_select_edge_check_internal(scene, bm, l_iter)) {
749 if (!uvedit_vert_is_edge_select_any_other(scene->toolsettings, bm, l, offsets)) {
750 uvedit_uv_select_shared_vert(scene, bm, l_iter, false, UV_STICKY_LOCATION, offsets);
751 }
752 if (!uvedit_vert_is_edge_select_any_other(scene->toolsettings, bm, l->next, offsets)) {
754 scene, bm, l_iter->next, false, UV_STICKY_LOCATION, offsets);
755 }
756 }
757 }
758 } while (((l_iter = l_iter->radial_next) != l) && (sticky_flag != UV_STICKY_LOCATION));
759}
760
762 BMesh *bm,
763 BMLoop *l,
764 const bool select,
765 const int sticky_flag,
766 const BMUVOffsets &offsets)
767{
768 const ToolSettings *ts = scene->toolsettings;
769 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0) {
770 BLI_assert(offsets.uv >= 0);
771 }
772 BMLoop *l_iter = l;
773 do {
774 if (uvedit_face_visible_test(scene, l_iter->f)) {
775 if ((sticky_flag == UV_STICKY_VERT) || BM_loop_uv_share_edge_check(l, l_iter, offsets.uv)) {
776 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
778 }
779 else {
781 }
782 }
783 }
784 } while (((l_iter = l_iter->radial_next) != l) && (sticky_flag != UV_STICKY_DISABLE));
785}
786
787void uvedit_edge_select_set(const Scene *scene, BMesh *bm, BMLoop *l, const bool select)
788{
789 if (select) {
791 }
792 else {
794 }
795}
796
798
799{
800 const ToolSettings *ts = scene->toolsettings;
801 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
803 if (ts->selectmode & SCE_SELECT_FACE) {
804 BM_face_select_set(bm, l->f, true);
805 }
806 else if (ts->selectmode & SCE_SELECT_EDGE) {
807 BM_edge_select_set(bm, l->e, true);
808 }
809 else {
810 BM_vert_select_set(bm, l->e->v1, true);
811 BM_vert_select_set(bm, l->e->v2, true);
812 }
813 }
814 else {
816 }
817 }
818 else {
820 uvedit_vert_select_set_no_sync(ts, bm, l->next, true);
822 }
823}
824
826{
827 const ToolSettings *ts = scene->toolsettings;
828
829 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
831 if (ts->selectmode & SCE_SELECT_FACE) {
832 BM_face_select_set(bm, l->f, false);
833 }
834 else if (ts->selectmode & SCE_SELECT_EDGE) {
835 BM_edge_select_set(bm, l->e, false);
836 }
837 else {
838 BM_vert_select_set(bm, l->e->v1, false);
839 BM_vert_select_set(bm, l->e->v2, false);
840 }
841 }
842 else {
844 if ((ts->selectmode & SCE_SELECT_VERTEX) == 0) {
845 /* Deselect UV vertex if not part of another edge selection */
848 }
851 }
852 }
853 else {
856 }
857 }
858 }
859 else {
861 if ((ts->uv_selectmode & UV_SELECT_VERT) == 0) {
862 /* Deselect UV vertex if not part of another edge selection */
863 if (!uvedit_edge_select_get_no_sync(ts, bm, l->next)) {
864 uvedit_vert_select_set_no_sync(ts, bm, l->next, false);
865 }
866 if (!uvedit_edge_select_get_no_sync(ts, bm, l->prev)) {
868 }
869 }
870 else {
872 uvedit_vert_select_set_no_sync(ts, bm, l->next, false);
873 }
874 }
875}
876
878 const BMesh *bm,
879 const BMLoop *l,
880 const BMUVOffsets &offsets)
881{
882 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
883
884 if ((bm->uv_select_sync_valid == false) && (ts->selectmode == SCE_SELECT_FACE)) {
885 /* Face only is a special case that can respect sticky modes. */
886 switch (ts->uv_sticky) {
887 case UV_STICKY_LOCATION: {
889 return true;
890 }
891 if (uvedit_vert_is_face_select_any_other(ts, bm, l, offsets)) {
892 return true;
893 }
894 return false;
895 }
896 case UV_STICKY_DISABLE: {
898 }
899 default: {
900 /* #UV_STICKY_VERT */
902 }
903 }
905 }
906
907 if (bm->uv_select_sync_valid) {
908 /* Pass. */
909 }
910 else if ((ts->selectmode & ~SCE_SELECT_FACE) == SCE_SELECT_EDGE) {
911 /* Edge/Face is a special case that can respect sticky modes. */
912 switch (ts->uv_sticky) {
913 case UV_STICKY_LOCATION: {
915 return true;
916 }
917 if (uvedit_vert_is_edge_select_any_other(ts, bm, l, offsets)) {
918 return true;
919 }
920 return false;
921 }
922 case UV_STICKY_DISABLE: {
923 return BM_elem_flag_test(l->e, BM_ELEM_SELECT) ||
925 }
926 default: {
927 /* #UV_STICKY_VERT */
929 }
930 }
932 }
933
934 if (bm->uv_select_sync_valid == false || ED_uvedit_sync_uvselect_ignore(ts)) {
935 if (ts->selectmode & SCE_SELECT_FACE) {
937 }
938 if (ts->selectmode & SCE_SELECT_EDGE) {
939 /* Are you looking for `uvedit_edge_select_test(...)` instead? */
940 }
942 }
944 }
945
946 if (ts->selectmode & SCE_SELECT_FACE) {
947 /* Are you looking for `uvedit_face_select_test(...)` instead? */
948 }
949
950 if (ts->selectmode & SCE_SELECT_EDGE) {
951 /* Are you looking for `uvedit_edge_select_test(...)` instead? */
952 }
953
955}
956
957bool uvedit_uv_select_test(const Scene *scene,
958 const BMesh *bm,
959 const BMLoop *l,
960 const BMUVOffsets &offsets)
961{
962 return uvedit_uv_select_test_ex(scene->toolsettings, bm, l, offsets);
963}
964
966 const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
967{
968 const ToolSettings *ts = scene->toolsettings;
969 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
972 return;
973 }
974 }
975
976 const int sticky = ts->uv_sticky;
977 switch (sticky) {
978 case UV_STICKY_DISABLE: {
979 if (uvedit_face_visible_test(scene, l->f)) {
981 }
982 break;
983 }
984 case UV_STICKY_VERT: {
986 break;
987 }
988 default: {
989 /* UV_STICKY_LOCATION. */
991 break;
992 }
993 }
994}
995
997 BMesh *bm,
998 BMLoop *l,
999 const bool select,
1000 const int sticky_flag,
1001
1002 const BMUVOffsets &offsets)
1003{
1005 BLI_assert(offsets.uv >= 0);
1006
1007 BMEdge *e_first, *e_iter;
1008 e_first = e_iter = l->e;
1009 do {
1010 BMLoop *l_radial_iter = e_iter->l;
1011 if (!l_radial_iter) {
1012 continue; /* Skip wire edges with no loops. */
1013 }
1014 do {
1015 if (l_radial_iter->v == l->v) {
1016 if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
1017 bool do_select = false;
1018 if (sticky_flag == UV_STICKY_VERT) {
1019 do_select = true;
1020 }
1021 else if (BM_loop_uv_share_vert_check(l, l_radial_iter, offsets.uv)) {
1022 do_select = true;
1023 }
1024
1025 if (do_select) {
1026 uvedit_uv_select_set(scene, bm, l_radial_iter, select);
1027 }
1028 }
1029 }
1030 } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
1031 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
1032}
1033
1034void uvedit_uv_select_set(const Scene *scene, BMesh *bm, BMLoop *l, const bool select)
1035{
1036 if (select) {
1037 uvedit_uv_select_enable(scene, bm, l);
1038 }
1039 else {
1040 uvedit_uv_select_disable(scene, bm, l);
1041 }
1042}
1043
1045{
1046 const ToolSettings *ts = scene->toolsettings;
1047
1048 if (ts->selectmode & SCE_SELECT_EDGE) {
1049 /* Are you looking for `uvedit_edge_select_set(...)` instead? */
1050 }
1051
1052 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
1054 if (ts->selectmode & SCE_SELECT_FACE) {
1055 BM_face_select_set(bm, l->f, true);
1056 }
1057 else {
1058 BM_vert_select_set(bm, l->v, true);
1059 }
1060 }
1061 else {
1063 }
1064 }
1065 else {
1067 }
1068}
1069
1071{
1072 const ToolSettings *ts = scene->toolsettings;
1073
1074 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
1076 if (ts->selectmode & SCE_SELECT_FACE) {
1077 BM_face_select_set(bm, l->f, false);
1078 }
1079 else {
1080 BM_vert_select_set(bm, l->v, false);
1081 }
1082 }
1083 else {
1085 }
1086 }
1087 else {
1088 uvedit_vert_select_set_no_sync(ts, bm, l, false);
1089 }
1090}
1091
1093 BMLoop *l_src,
1094 const BMUVOffsets &offsets)
1095{
1096 BLI_assert(offsets.uv >= 0);
1097 BMLoop *l_other = nullptr;
1098 BMLoop *l_iter = l_src->radial_next;
1099 if (l_iter != l_src) {
1100 do {
1101 if (uvedit_face_visible_test(scene, l_iter->f) &&
1102 BM_loop_uv_share_edge_check(l_src, l_iter, offsets.uv))
1103 {
1104 /* Check UVs are contiguous. */
1105 if (l_other == nullptr) {
1106 l_other = l_iter;
1107 }
1108 else {
1109 /* Only use when there is a single alternative. */
1110 l_other = nullptr;
1111 break;
1112 }
1113 }
1114 } while ((l_iter = l_iter->radial_next) != l_src);
1115 }
1116 return l_other;
1117}
1118
1120 BMLoop *l_edge,
1121 BMVert *v_pivot,
1122 const BMUVOffsets &offsets)
1123{
1125 nullptr);
1126
1127 BMLoop *l_step = l_edge;
1128 l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
1129 BMLoop *l_step_last = nullptr;
1130 do {
1131 BLI_assert(BM_vert_in_edge(l_step->e, v_pivot));
1132 l_step_last = l_step;
1133 l_step = uvedit_loop_find_other_radial_loop_with_visible_face(scene, l_step, offsets);
1134 if (l_step) {
1135 l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
1136 }
1137 } while (l_step != nullptr);
1138
1139 if (l_step_last != nullptr) {
1141 nullptr);
1142 }
1143
1144 return l_step_last;
1145}
1146
1148
1149/* -------------------------------------------------------------------- */
1152
1154{
1155 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
1156 BLI_assert(bm->uv_select_sync_valid);
1159 }
1160 return uvedit_vert_select_get_no_sync(ts, bm, l);
1161}
1162
1164{
1165 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
1166 BLI_assert(bm->uv_select_sync_valid);
1168 /* Caller checks for visibility. */
1171 }
1172 return uvedit_edge_select_get_no_sync(ts, bm, l);
1173}
1174
1176 const BMesh *bm,
1177 BMLoop *l,
1178 const bool select)
1179{
1180 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
1181 BLI_assert(bm->uv_select_sync_valid);
1184 return;
1185 }
1187}
1188
1190 const BMesh *bm,
1191 BMLoop *l,
1192 const bool select)
1193{
1194 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
1195 BLI_assert(bm->uv_select_sync_valid);
1198 return;
1199 }
1201}
1202
1204
1205/* -------------------------------------------------------------------- */
1208
1209UvNearestHit uv_nearest_hit_init_dist_px(const View2D *v2d, const float dist_px)
1210{
1211 UvNearestHit hit = {nullptr};
1212 hit.dist_sq = square_f(U.pixelsize * dist_px);
1213 hit.scale[0] = UI_view2d_scale_get_x(v2d);
1214 hit.scale[1] = UI_view2d_scale_get_y(v2d);
1215 return hit;
1216}
1217
1219{
1220 UvNearestHit hit = {nullptr};
1221 hit.dist_sq = FLT_MAX;
1222 hit.scale[0] = UI_view2d_scale_get_x(v2d);
1223 hit.scale[1] = UI_view2d_scale_get_y(v2d);
1224 return hit;
1225}
1226
1228{
1229 UvNearestHit hit = {nullptr};
1230 hit.dist_sq = FLT_MAX;
1231 hit.scale[0] = 1.0f;
1232 hit.scale[1] = 1.0f;
1233 return hit;
1234}
1235
1237 Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
1238{
1239 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
1240 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1241 BMFace *efa;
1242 BMLoop *l;
1243 BMIter iter, liter;
1244 float *luv, *luv_next;
1245 int i;
1246 bool found = false;
1247
1248 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1249 BLI_assert(offsets.uv >= 0);
1250
1252
1253 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1254 if (!uvedit_face_visible_test(scene, efa)) {
1255 continue;
1256 }
1257 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1258 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
1259 luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, offsets.uv);
1260
1261 float delta[2];
1262 closest_to_line_segment_v2(delta, co, luv, luv_next);
1263
1264 sub_v2_v2(delta, co);
1265 mul_v2_v2(delta, hit->scale);
1266
1267 float dist_test_sq = len_squared_v2(delta);
1268
1269 /* Ensures that successive selection attempts will select other edges sharing the same
1270 * UV coordinates as the previous selection. */
1271 if ((penalty != 0.0f) && uvedit_edge_select_test(scene, bm, l, offsets)) {
1272 dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty);
1273 }
1274 if (dist_test_sq < hit->dist_sq) {
1275 hit->ob = obedit;
1276 hit->efa = efa;
1277
1278 hit->l = l;
1279
1280 hit->dist_sq = dist_test_sq;
1281 found = true;
1282 }
1283 }
1284 }
1285 return found;
1286}
1287
1289 const Span<Object *> objects,
1290 const float co[2],
1291 const float penalty,
1292 UvNearestHit *hit)
1293{
1294 bool found = false;
1295 for (Object *obedit : objects) {
1296 if (uv_find_nearest_edge(scene, obedit, co, penalty, hit)) {
1297 found = true;
1298 }
1299 }
1300 return found;
1301}
1302
1304 Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit, const bool only_in_face)
1305{
1306 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
1307 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1308 bool found = false;
1309
1310 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
1311
1312 BMIter iter;
1313 BMFace *efa;
1314
1315 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1316 if (!uvedit_face_visible_test(scene, efa)) {
1317 continue;
1318 }
1319
1320 float cent[2];
1321 BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent);
1322
1323 float delta[2];
1324 sub_v2_v2v2(delta, co, cent);
1325 mul_v2_v2(delta, hit->scale);
1326
1327 const float dist_test_sq = len_squared_v2(delta);
1328
1329 if (dist_test_sq < hit->dist_sq) {
1330
1331 if (only_in_face) {
1332 if (!BM_face_uv_point_inside_test(efa, co, cd_loop_uv_offset)) {
1333 continue;
1334 }
1335 }
1336
1337 hit->ob = obedit;
1338 hit->efa = efa;
1339 hit->dist_sq = dist_test_sq;
1340 found = true;
1341 }
1342 }
1343 return found;
1344}
1345
1346bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit)
1347{
1348 return uv_find_nearest_face_ex(scene, obedit, co, hit, false);
1349}
1350
1352 const Span<Object *> objects,
1353 const float co[2],
1354 UvNearestHit *hit,
1355 const bool only_in_face)
1356{
1357 bool found = false;
1358 for (Object *obedit : objects) {
1359 if (uv_find_nearest_face_ex(scene, obedit, co, hit, only_in_face)) {
1360 found = true;
1361 }
1362 }
1363 return found;
1364}
1365
1367 const Span<Object *> objects,
1368 const float co[2],
1369 UvNearestHit *hit)
1370{
1371 return uv_find_nearest_face_multi_ex(scene, objects, co, hit, false);
1372}
1373
1374static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset)
1375{
1376 const float *uv_prev = BM_ELEM_CD_GET_FLOAT_P(l->prev, cd_loop_uv_offset);
1377 const float *uv_curr = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1378 const float *uv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
1379
1380 return ((line_point_side_v2(uv_prev, uv_curr, co) > 0.0f) &&
1381 (line_point_side_v2(uv_next, uv_curr, co) <= 0.0f));
1382}
1383
1385 Scene *scene, Object *obedit, float const co[2], const float penalty_dist, UvNearestHit *hit)
1386{
1387 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
1388 bool found = false;
1389
1390 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1391 BMFace *efa;
1392 BMIter iter;
1393
1395
1396 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1397 BLI_assert(offsets.uv >= 0);
1398
1399 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1400 if (!uvedit_face_visible_test(scene, efa)) {
1401 continue;
1402 }
1403
1404 BMIter liter;
1405 BMLoop *l;
1406 int i;
1407 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1408 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
1409
1410 float delta[2];
1411
1412 sub_v2_v2v2(delta, co, luv);
1413 mul_v2_v2(delta, hit->scale);
1414
1415 float dist_test_sq = len_squared_v2(delta);
1416
1417 /* Ensures that successive selection attempts will select other vertices sharing the same
1418 * UV coordinates */
1419 if ((penalty_dist != 0.0f) && uvedit_uv_select_test(scene, bm, l, offsets)) {
1420 dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty_dist);
1421 }
1422
1423 if (dist_test_sq <= hit->dist_sq) {
1424 if (dist_test_sq == hit->dist_sq) {
1425 if (!uv_nearest_between(l, co, offsets.uv)) {
1426 continue;
1427 }
1428 }
1429
1430 hit->dist_sq = dist_test_sq;
1431
1432 hit->ob = obedit;
1433 hit->efa = efa;
1434 hit->l = l;
1435 found = true;
1436 }
1437 }
1438 }
1439
1440 return found;
1441}
1442
1444 const Span<Object *> objects,
1445 float const co[2],
1446 const float penalty_dist,
1447 UvNearestHit *hit)
1448{
1449 bool found = false;
1450 for (Object *obedit : objects) {
1451 if (uv_find_nearest_vert(scene, obedit, co, penalty_dist, hit)) {
1452 found = true;
1453 }
1454 }
1455 return found;
1456}
1457
1458static bool uvedit_nearest_uv(const Scene *scene,
1459 Object *obedit,
1460 const float co[2],
1461 const float scale[2],
1462 const bool ignore_selected,
1463 float *dist_sq,
1464 float r_uv[2])
1465{
1466 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1467 BMIter iter;
1468 BMFace *efa;
1469 const float *uv_best = nullptr;
1470 float dist_best = *dist_sq;
1471 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1472 BLI_assert(offsets.uv >= 0);
1473 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1474 if (!uvedit_face_visible_test(scene, efa)) {
1475 continue;
1476 }
1477 BMLoop *l_iter, *l_first;
1478 l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
1479 do {
1480 if (ignore_selected && uvedit_uv_select_test(scene, bm, l_iter, offsets)) {
1481 continue;
1482 }
1483
1484 const float *uv = BM_ELEM_CD_GET_FLOAT_P(l_iter, offsets.uv);
1485 float co_tmp[2];
1486 mul_v2_v2v2(co_tmp, scale, uv);
1487 const float dist_test = len_squared_v2v2(co, co_tmp);
1488 if (dist_best > dist_test) {
1489 dist_best = dist_test;
1490 uv_best = uv;
1491 }
1492 } while ((l_iter = l_iter->next) != l_first);
1493 }
1494
1495 if (uv_best != nullptr) {
1496 copy_v2_v2(r_uv, uv_best);
1497 *dist_sq = dist_best;
1498 return true;
1499 }
1500 return false;
1501}
1502
1504 const Scene *scene,
1505 const Span<Object *> objects,
1506 const float mval_fl[2],
1507 const bool ignore_selected,
1508 float *dist_sq,
1509 float r_uv[2])
1510{
1511 bool found = false;
1512
1513 float scale[2], offset[2];
1514 UI_view2d_scale_get(v2d, &scale[0], &scale[1]);
1515 UI_view2d_view_to_region_fl(v2d, 0.0f, 0.0f, &offset[0], &offset[1]);
1516
1517 float co[2];
1518 sub_v2_v2v2(co, mval_fl, offset);
1519
1520 for (Object *obedit : objects) {
1521 if (uvedit_nearest_uv(scene, obedit, co, scale, ignore_selected, dist_sq, r_uv)) {
1522 found = true;
1523 }
1524 }
1525 return found;
1526}
1527
1529
1530/* -------------------------------------------------------------------- */
1537
1538BMLoop *uv_find_nearest_loop_from_vert(Scene *scene, Object *obedit, BMVert *v, const float co[2])
1539{
1540 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1541 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
1542
1543 BMIter liter;
1544 BMLoop *l;
1545 BMLoop *l_found = nullptr;
1546 float dist_best_sq = FLT_MAX;
1547
1548 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
1549 if (!uvedit_face_visible_test(scene, l->f)) {
1550 continue;
1551 }
1552
1553 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1554 const float dist_test_sq = len_squared_v2v2(co, luv);
1555 if (dist_test_sq < dist_best_sq) {
1556 dist_best_sq = dist_test_sq;
1557 l_found = l;
1558 }
1559 }
1560 return l_found;
1561}
1562
1563BMLoop *uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, const float co[2])
1564{
1565 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1566 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
1567
1568 BMIter eiter;
1569 BMLoop *l;
1570 BMLoop *l_found = nullptr;
1571 float dist_best_sq = FLT_MAX;
1572
1573 BM_ITER_ELEM (l, &eiter, e, BM_LOOPS_OF_EDGE) {
1574 if (!uvedit_face_visible_test(scene, l->f)) {
1575 continue;
1576 }
1577 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1578 const float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
1579 const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv, luv_next);
1580 if (dist_test_sq < dist_best_sq) {
1581 dist_best_sq = dist_test_sq;
1582 l_found = l;
1583 }
1584 }
1585 return l_found;
1586}
1587
1589
1590/* -------------------------------------------------------------------- */
1593
1594static bool uvedit_select_pin_ok_or_report(const Scene *scene, ReportList *reports)
1595{
1597 BKE_report(reports, RPT_ERROR, "Pinned vertices can be selected in Vertex Mode only");
1598 return false;
1599 }
1600 return true;
1601}
1602
1604{
1605 const ToolSettings *ts = scene->toolsettings;
1608 const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2);
1609 BLI_assert(active_uv_name);
1610 UNUSED_VARS_NDEBUG(active_uv_name);
1611
1612 /* Needed because this data must *not* be used for select-sync
1613 * once this has been manipulated with select-sync disabled. */
1615}
1616
1621
1622/* We may want to use this eventually. */
1624{
1625 const ToolSettings *ts = scene->toolsettings;
1626 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
1628 }
1629 else {
1631 }
1632}
1633
1635 const BMesh *bm,
1636 const BMLoop *l,
1637 const BMUVOffsets &offsets)
1638{
1639 BLI_assert(offsets.uv >= 0);
1640 BMEdge *e_iter = l->e;
1641 do {
1642 BMLoop *l_radial_iter = e_iter->l;
1643 if (!l_radial_iter) {
1644 continue; /* Skip wire edges with no loops. */
1645 }
1646 do {
1647 if (!uvedit_face_visible_test_ex(ts, l_radial_iter->f)) {
1648 continue;
1649 }
1650 /* Use #l_other to check if the uvs are connected (share the same uv coordinates)
1651 * and #l_radial_iter for the actual edge selection test. */
1652 BMLoop *l_other = (l_radial_iter->v != l->v) ? l_radial_iter->next : l_radial_iter;
1653 if (BM_loop_uv_share_vert_check(l, l_other, offsets.uv) &&
1654 uvedit_edge_select_test_ex(ts, bm, l_radial_iter, offsets))
1655 {
1656 return true;
1657 }
1658 } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
1659 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != l->e);
1660
1661 return false;
1662}
1663
1665 const BMesh *bm,
1666 const BMLoop *l,
1667 const BMUVOffsets &offsets)
1668{
1669 BLI_assert(offsets.uv >= 0);
1670 BMLoop *l_radial_iter = l->radial_next;
1671 if (l_radial_iter == l) {
1672 return false;
1673 }
1674 do {
1675 if (!uvedit_face_visible_test_ex(ts, l_radial_iter->f)) {
1676 continue;
1677 }
1678 if (BM_loop_uv_share_edge_check(l, l_radial_iter, offsets.uv) &&
1679 uvedit_face_select_test_ex(ts, bm, l_radial_iter->f))
1680 {
1681 return true;
1682 }
1683 } while ((l_radial_iter = l_radial_iter->radial_next) != l);
1684
1685 return false;
1686}
1687
1689 const BMesh *bm,
1690 const BMLoop *l,
1691 const BMUVOffsets &offsets)
1692{
1693 BLI_assert(offsets.uv >= 0);
1694 BMIter liter;
1695 BMLoop *l_iter;
1696 BM_ITER_ELEM (l_iter, &liter, l->v, BM_LOOPS_OF_VERT) {
1697 if (!uvedit_face_visible_test_ex(ts, l_iter->f) || (l_iter->f == l->f)) {
1698 continue;
1699 }
1700 if (BM_loop_uv_share_vert_check(l, l_iter, offsets.uv) &&
1701 uvedit_face_select_test_ex(ts, bm, l_iter->f))
1702 {
1703 return true;
1704 }
1705 }
1706 return false;
1707}
1708
1710 const BMesh *bm,
1711 const BMLoop *l,
1712 const BMUVOffsets &offsets)
1713{
1714 BLI_assert(offsets.uv >= 0);
1715 BMIter liter;
1716 BMLoop *l_iter;
1717 BM_ITER_ELEM (l_iter, &liter, l->v, BM_LOOPS_OF_VERT) {
1718 if ((l_iter->f == l->f) || !uvedit_face_visible_test_ex(ts, l_iter->f)) {
1719 continue;
1720 }
1721 if (BM_loop_uv_share_vert_check(l, l_iter, offsets.uv) &&
1722 !uvedit_face_select_test_ex(ts, bm, l_iter->f))
1723 {
1724 return false;
1725 }
1726 }
1727 return true;
1728}
1729
1730static void bm_clear_uv_vert_selection(const Scene *scene, BMesh *bm)
1731{
1732 const ToolSettings *ts = scene->toolsettings;
1733 BMFace *efa;
1734 BMLoop *l;
1735 BMIter iter, liter;
1736 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1737 if (!uvedit_face_visible_test(scene, efa)) {
1738 continue;
1739 }
1740 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1741 uvedit_vert_select_set_no_sync(ts, bm, l, false);
1742 }
1743 }
1744}
1745
1747
1748/* -------------------------------------------------------------------- */
1756
1758{
1759 BLI_assert(bm && (ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0);
1762}
1770{
1771 BLI_assert(bm && (ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0);
1774}
1775
1777 const BMesh *bm,
1778 BMLoop *l,
1779 bool select)
1780{
1781 BLI_assert(bm && (ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0);
1782 BLI_assert(bm->uv_select_sync_valid == false); /* #uvedit_select_prepare_custom_data ensures. */
1786}
1788 const BMesh *bm,
1789 BMLoop *l,
1790 bool select)
1791{
1792 BLI_assert(bm && (ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0);
1793 BLI_assert(bm->uv_select_sync_valid == false); /* #uvedit_select_prepare_custom_data ensures. */
1797}
1798
1800 const BMesh *bm,
1801 BMFace *f,
1802 bool select)
1803{
1804 BLI_assert(bm && (ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0);
1805 BLI_assert(bm->uv_select_sync_valid == false); /* #uvedit_select_prepare_custom_data ensures. */
1809}
1810
1812
1813/* -------------------------------------------------------------------- */
1819
1820namespace blender::ed::uv {
1821
1822std::unique_ptr<UVSyncSelectFromMesh> UVSyncSelectFromMesh::create_if_needed(
1823 const ToolSettings &ts, BMesh &bm)
1824{
1825 if ((ts.uv_flag & UV_FLAG_SELECT_SYNC) == 0) {
1826 return nullptr;
1827 }
1829 return nullptr;
1830 }
1831 if (bm.uv_select_sync_valid == false) {
1832 return nullptr;
1833 }
1834 const int cd_loop_uv_offset = CustomData_get_active_layer(&bm.ldata, CD_PROP_FLOAT2);
1835 if (cd_loop_uv_offset == -1) {
1836 return nullptr;
1837 }
1838 return std::make_unique<UVSyncSelectFromMesh>(bm, ts.uv_sticky);
1839}
1840
1842{
1843 const int cd_loop_uv_offset = CustomData_get_active_layer(&bm_.ldata, CD_PROP_FLOAT2);
1844 BLI_assert(cd_loop_uv_offset != -1);
1845
1846 const bool shared = uv_sticky_ == UV_STICKY_LOCATION;
1847 const BMUVSelectPickParams uv_pick_params = {
1848 /*cd_loop_uv_offset*/ cd_loop_uv_offset,
1849 /*shared*/ shared,
1850 };
1851
1853 &bm_, false, uv_pick_params, bm_verts_deselect_, bm_edges_deselect_, bm_faces_deselect_);
1854
1856 &bm_, true, uv_pick_params, bm_verts_select_, bm_edges_select_, bm_faces_select_);
1857}
1858
1859/* Select. */
1860
1862{
1863 bm_verts_select_.append(v);
1864}
1866{
1867 bm_edges_select_.append(f);
1868}
1870{
1871 bm_faces_select_.append(f);
1872}
1873
1874/* De-Select. */
1875
1877{
1878 bm_verts_deselect_.append(v);
1879}
1881{
1882 bm_edges_deselect_.append(f);
1883}
1885{
1886 bm_faces_deselect_.append(f);
1887}
1888
1889/* Select set. */
1890
1892{
1893 if (value) {
1894 bm_verts_select_.append(v);
1895 }
1896 else {
1897 bm_verts_deselect_.append(v);
1898 }
1899}
1901{
1902 if (value) {
1903 bm_edges_select_.append(f);
1904 }
1905 else {
1906 bm_edges_deselect_.append(f);
1907 }
1908}
1910{
1911 if (value) {
1912 bm_faces_select_.append(f);
1913 }
1914 else {
1915 bm_faces_deselect_.append(f);
1916 }
1917}
1918
1919} // namespace blender::ed::uv
1920
1922
1923/* -------------------------------------------------------------------- */
1927
1929{
1930 const ToolSettings *ts = scene->toolsettings;
1933
1935
1936 /* Vertex Mode only. */
1937 if (ts->uv_selectmode & UV_SELECT_VERT) {
1938 BMFace *efa;
1939 BMLoop *l;
1940 BMIter iter, liter;
1941 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1942 if (!uvedit_face_visible_test(scene, efa)) {
1943 continue;
1944 }
1945 bool select_all = true;
1946 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1947 bool edge_selected = uvedit_vert_select_get_no_sync(ts, bm, l) &&
1949 uvedit_edge_select_set_no_sync(ts, bm, l, edge_selected);
1950 if (!edge_selected) {
1951 select_all = false;
1952 }
1953 }
1954 uvedit_face_select_set_no_sync(ts, bm, efa, select_all);
1955 }
1956 }
1957 else if (ts->uv_selectmode & UV_SELECT_EDGE) {
1958 BMFace *efa;
1959 BMLoop *l;
1960 BMIter iter, liter;
1961 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1962 if (!uvedit_face_visible_test(scene, efa)) {
1963 continue;
1964 }
1965 bool select_all = true;
1966 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1967 if (!uvedit_edge_select_get_no_sync(ts, bm, l)) {
1968 select_all = false;
1969 break;
1970 }
1971 }
1972 uvedit_face_select_set_no_sync(ts, bm, efa, select_all);
1973 }
1974 }
1975}
1976
1978
1979/* -------------------------------------------------------------------- */
1982
1983void uvedit_select_flush_from_verts(const Scene *scene, BMesh *bm, const bool select)
1984{
1985 const ToolSettings *ts = scene->toolsettings;
1989
1990 if (select) {
1991 /* Careful when using this in face select mode.
1992 * For face selections with sticky mode enabled, this can create invalid selection states. */
1993 BMFace *efa;
1994 BMLoop *l;
1995 BMIter iter, liter;
1996 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1997 if (!uvedit_face_visible_test(scene, efa)) {
1998 continue;
1999 }
2000 bool select_all = true;
2001 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2004 {
2006 }
2007 else {
2008 select_all = false;
2009 }
2010 }
2011 if (select_all) {
2012 uvedit_face_select_set_no_sync(ts, bm, efa, true);
2013 }
2014 }
2015 }
2016 else {
2017 BMFace *efa;
2018 BMLoop *l;
2019 BMIter iter, liter;
2020 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2021 if (!uvedit_face_visible_test(scene, efa)) {
2022 continue;
2023 }
2024 bool select_all = true;
2025 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2026 if (!uvedit_vert_select_get_no_sync(ts, bm, l) ||
2027 !uvedit_vert_select_get_no_sync(ts, bm, l->next))
2028 {
2029 uvedit_edge_select_set_no_sync(ts, bm, l, false);
2030 select_all = false;
2031 }
2032 }
2033 if (select_all == false) {
2034 uvedit_face_select_set_no_sync(ts, bm, efa, false);
2035 }
2036 }
2037 }
2038}
2039
2041
2042/* -------------------------------------------------------------------- */
2045
2053
2055 BMLoop *l_step,
2056 BMVert *v_from,
2057 const BMUVOffsets &offsets)
2058{
2059 if (l_step->f->len == 4) {
2060 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
2061 BMLoop *l_step_over = (v_from == l_step->v) ? l_step->next : l_step->prev;
2063 scene, l_step_over, offsets);
2064 if (l_step_over) {
2065 return (l_step_over->v == v_from_next) ? l_step_over->prev : l_step_over->next;
2066 }
2067 }
2068 return nullptr;
2069}
2070
2072 BMLoop *l_step,
2073 BMVert *v_from,
2074 const BMUVOffsets &offsets)
2075{
2076 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
2078 scene, l_step, v_from_next, offsets);
2079}
2080
2081/* TODO(@ideasman42): support this in the BMesh API, as we have for clearing other types. */
2083{
2084 BMIter iter;
2085 BMFace *f;
2086 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
2087 BMIter liter;
2088 BMLoop *l_iter;
2089 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
2091 }
2092 }
2093}
2094
2099 BMesh *bm,
2100 BMLoop *l_init_pair[2],
2101 const BMUVOffsets &offsets)
2102{
2104
2105 for (int side = 0; side < 2; side++) {
2106 BMLoop *l_step_pair[2] = {l_init_pair[0], l_init_pair[1]};
2107 BMVert *v_from = side ? l_step_pair[0]->e->v1 : l_step_pair[0]->e->v2;
2108 /* Disable since we start from the same edge. */
2109 BM_elem_flag_disable(l_step_pair[0], BM_ELEM_TAG);
2110 BM_elem_flag_disable(l_step_pair[1], BM_ELEM_TAG);
2111 while ((l_step_pair[0] != nullptr) && (l_step_pair[1] != nullptr)) {
2112 if (!uvedit_face_visible_test(scene, l_step_pair[0]->f) ||
2113 !uvedit_face_visible_test(scene, l_step_pair[1]->f) ||
2114 /* Check loops have not diverged. */
2115 (uvedit_loop_find_other_radial_loop_with_visible_face(scene, l_step_pair[0], offsets) !=
2116 l_step_pair[1]))
2117 {
2118 break;
2119 }
2120
2121 BLI_assert(l_step_pair[0]->e == l_step_pair[1]->e);
2122
2123 BM_elem_flag_enable(l_step_pair[0], BM_ELEM_TAG);
2124 BM_elem_flag_enable(l_step_pair[1], BM_ELEM_TAG);
2125
2126 BMVert *v_from_next = BM_edge_other_vert(l_step_pair[0]->e, v_from);
2127 /* Walk over both sides, ensure they keep on the same edge. */
2128 for (int i = 0; i < ARRAY_SIZE(l_step_pair); i++) {
2130 scene, l_step_pair[i], v_from, offsets);
2131 }
2132
2133 if ((l_step_pair[0] && BM_elem_flag_test(l_step_pair[0], BM_ELEM_TAG)) ||
2134 (l_step_pair[1] && BM_elem_flag_test(l_step_pair[1], BM_ELEM_TAG)))
2135 {
2136 break;
2137 }
2138 v_from = v_from_next;
2139 }
2140 }
2141}
2142
2150 BMesh *bm,
2151 BMLoop *l_init,
2152 const BMUVOffsets &offsets,
2153 enum eUVEdgeLoopBoundaryMode boundary_mode,
2154 int r_count_by_select[2])
2155{
2156 if (r_count_by_select) {
2157 r_count_by_select[0] = r_count_by_select[1] = 0;
2158 }
2159
2161
2162 for (int side = 0; side < 2; side++) {
2163 BMLoop *l_step = l_init;
2164 BMVert *v_from = side ? l_step->e->v1 : l_step->e->v2;
2165 /* Disable since we start from the same edge. */
2167 while (l_step != nullptr) {
2168
2169 if (!uvedit_face_visible_test(scene, l_step->f) ||
2170 /* Check the boundary is still a boundary. */
2172 nullptr))
2173 {
2174 break;
2175 }
2176
2177 if (r_count_by_select != nullptr) {
2178 r_count_by_select[uvedit_edge_select_test(scene, bm, l_step, offsets)] += 1;
2179 /* Early exit when mixed could be optional if needed. */
2180 if (r_count_by_select[0] && r_count_by_select[1]) {
2181 r_count_by_select[0] = r_count_by_select[1] = -1;
2182 break;
2183 }
2184 }
2185
2187
2188 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
2189 BMFace *f_step_prev = l_step->f;
2190
2191 l_step = bm_select_edgeloop_single_side_next(scene, l_step, v_from, offsets);
2192
2193 if (l_step && BM_elem_flag_test(l_step, BM_ELEM_TAG)) {
2194 break;
2195 }
2196 if (boundary_mode == UV_EDGE_LOOP_BOUNDARY_LOOP) {
2197 /* Don't allow walking over the face. */
2198 if (f_step_prev == l_step->f) {
2199 break;
2200 }
2201 }
2202 v_from = v_from_next;
2203 }
2204 }
2205}
2206
2207static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
2208{
2209 const ToolSettings *ts = scene->toolsettings;
2210 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2211 bool select;
2212
2213 /* NOTE: this is a special case, even when sync select is enabled,
2214 * the flags are used then flushed to the vertices.
2215 * So these need to be ensured even though the layers aren't used afterwards. */
2216 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2217
2218 if (extend) {
2219 select = !uvedit_edge_select_test(scene, bm, hit->l, offsets);
2220 }
2221 else {
2222 select = true;
2223 }
2224
2225 BMLoop *l_init_pair[2] = {
2226 hit->l,
2228 };
2229
2230 /* When selecting boundaries, support cycling between selection modes. */
2232
2233 /* Tag all loops that are part of the edge loop (select after).
2234 * This is done so we can */
2235 if (l_init_pair[1] == nullptr) {
2236 int count_by_select[2];
2237 /* If the loops selected toggle the boundaries. */
2239 scene, bm, l_init_pair[0], offsets, boundary_mode, count_by_select);
2240 if (count_by_select[!select] == 0) {
2241 boundary_mode = UV_EDGE_LOOP_BOUNDARY_ALL;
2242
2243 /* If the boundary is selected, toggle back to the loop. */
2245 scene, bm, l_init_pair[0], offsets, boundary_mode, count_by_select);
2246 if (count_by_select[!select] == 0) {
2247 boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP;
2248 }
2249 }
2250 }
2251
2252 if (l_init_pair[1] == nullptr) {
2253 uv_select_edgeloop_single_side_tag(scene, bm, l_init_pair[0], offsets, boundary_mode, nullptr);
2254 }
2255 else {
2256 uv_select_edgeloop_double_side_tag(scene, bm, l_init_pair, offsets);
2257 }
2258
2259 /* Apply the selection. */
2260 if (!extend) {
2261 ED_uvedit_deselect_all(scene, obedit, SEL_DESELECT);
2262 }
2263
2264 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2266 }
2267
2268 /* Select all tagged loops. */
2269 {
2270 BMIter iter;
2271 BMFace *f;
2272 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
2273 BMIter liter;
2274 BMLoop *l_iter;
2275 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
2276 if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
2277 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2278 uvedit_edge_select_set_with_sticky(scene, bm, l_iter, select, offsets);
2279 }
2280 else {
2281 if (ts->uv_selectmode == UV_SELECT_VERT) {
2282 uvedit_uv_select_set_with_sticky(scene, bm, l_iter, select, offsets);
2283 uvedit_uv_select_set_with_sticky(scene, bm, l_iter->next, select, offsets);
2284 }
2285 else {
2286 uvedit_edge_select_set_with_sticky(scene, bm, l_iter, select, offsets);
2287 }
2288 }
2289 }
2290 }
2291 }
2292 }
2293
2294 return select ? 1 : -1;
2295}
2296
2298
2299/* -------------------------------------------------------------------- */
2302
2303static int uv_select_faceloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
2304{
2305 const ToolSettings *ts = scene->toolsettings;
2306 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2307 bool select;
2308
2309 if (!extend) {
2310 ED_uvedit_deselect_all(scene, obedit, SEL_DESELECT);
2311 }
2312
2313 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2315 }
2316 else {
2318 }
2319 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2320
2322
2323 if (extend) {
2324 select = !uvedit_face_select_test(scene, bm, hit->l->f);
2325 }
2326 else {
2327 select = true;
2328 }
2329
2330 BMLoop *l_pair[2] = {
2331 hit->l,
2333 };
2334
2335 for (int side = 0; side < 2; side++) {
2336 BMLoop *l_step = l_pair[side];
2337 while (l_step) {
2338 if (!uvedit_face_visible_test(scene, l_step->f)) {
2339 break;
2340 }
2341
2342 uvedit_face_select_set_with_sticky(scene, bm, l_step->f, select, offsets);
2343
2345 if (l_step->f->len == 4) {
2346 BMLoop *l_step_opposite = l_step->next->next;
2348 scene, l_step_opposite, offsets);
2349 }
2350 else {
2351 l_step = nullptr;
2352 }
2353
2354 /* Break iteration when `l_step`:
2355 * - is the first loop where we started from.
2356 * - tagged using #BM_ELEM_TAG (meaning this loop has been visited in this iteration). */
2357 if (l_step && BM_elem_flag_test(l_step->f, BM_ELEM_TAG)) {
2358 break;
2359 }
2360 }
2361 }
2362
2363 return (select) ? 1 : -1;
2364}
2365
2367
2368/* -------------------------------------------------------------------- */
2371
2372static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
2373{
2374 const ToolSettings *ts = scene->toolsettings;
2375 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2376 const bool use_face_select = (ts->uv_flag & UV_FLAG_SELECT_SYNC) ?
2377 (ts->selectmode & SCE_SELECT_FACE) :
2379 const bool use_vertex_select = (ts->uv_flag & UV_FLAG_SELECT_SYNC) ?
2382 bool select;
2383
2384 if (!extend) {
2385 ED_uvedit_deselect_all(scene, obedit, SEL_DESELECT);
2386 }
2387
2388 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2390 }
2391 else {
2393 }
2394 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2395
2397
2398 if (extend) {
2399 select = !uvedit_edge_select_test(scene, bm, hit->l, offsets);
2400 }
2401 else {
2402 select = true;
2403 }
2404
2405 BMLoop *l_pair[2] = {
2406 hit->l,
2408 };
2409
2410 for (int side = 0; side < 2; side++) {
2411 BMLoop *l_step = l_pair[side];
2412 /* Disable since we start from the same edge. */
2414 while (l_step) {
2415 if (!uvedit_face_visible_test(scene, l_step->f)) {
2416 break;
2417 }
2418
2419 if (use_face_select) {
2420 /* While selecting face loops is now done in a separate function #uv_select_faceloop(),
2421 * this check is still kept for edge ring selection, to keep it consistent with how edge
2422 * ring selection works in face mode in the 3D viewport. */
2423 uvedit_face_select_set_with_sticky(scene, bm, l_step->f, select, offsets);
2424 }
2425 else if (use_vertex_select) {
2426 uvedit_uv_select_set_with_sticky(scene, bm, l_step, select, offsets);
2427 uvedit_uv_select_set_with_sticky(scene, bm, l_step->next, select, offsets);
2428 }
2429 else {
2430 /* Edge select mode */
2431 uvedit_edge_select_set_with_sticky(scene, bm, l_step, select, offsets);
2432 }
2433
2435 if (l_step->f->len == 4) {
2436 BMLoop *l_step_opposite = l_step->next->next;
2438 scene, l_step_opposite, offsets);
2439 if (l_step == nullptr) {
2440 /* Ensure we touch the opposite edge if we can't walk over it. */
2441 l_step = l_step_opposite;
2442 }
2443 }
2444 else {
2445 l_step = nullptr;
2446 }
2447
2448 /* Break iteration when `l_step`:
2449 * - Is the first loop where we started from.
2450 * - Tagged using #BM_ELEM_TAG (meaning this loop has been visited in this iteration).
2451 * - Has its corresponding UV edge selected/unselected based on #select. */
2452 if (l_step && BM_elem_flag_test(l_step->e, BM_ELEM_TAG)) {
2453 /* Previously this check was not done and this resulted in the final edge in the edge ring
2454 * cycle to be skipped during selection (caused by old sticky selection behavior). */
2455 if (select && uvedit_edge_select_test(scene, bm, l_step, offsets)) {
2456 break;
2457 }
2458 if (!select && !uvedit_edge_select_test(scene, bm, l_step, offsets)) {
2459 break;
2460 }
2461 }
2462 }
2463 }
2464
2465 return (select) ? 1 : -1;
2466}
2467
2469
2470/* -------------------------------------------------------------------- */
2473
2474static void uv_select_linked_multi(const Scene *scene,
2475 const Span<Object *> objects,
2476 UvNearestHit *hit,
2477 const bool extend,
2478 bool deselect,
2479 const bool toggle,
2480 const bool select_faces,
2481 const char hflag)
2482{
2483 if (select_faces) {
2485 }
2486 else {
2487 /* Tagging could be supported for other elements but currently isn't needed. */
2488 BLI_assert(hflag == BM_ELEM_SELECT);
2489 }
2490
2491 const ToolSettings *ts = scene->toolsettings;
2492 const bool uv_select_sync = (ts->uv_flag & UV_FLAG_SELECT_SYNC);
2493
2494 /* loop over objects, or just use hit->ob */
2495 for (const int ob_index : objects.index_range()) {
2496 if (hit && ob_index != 0) {
2497 break;
2498 }
2499 Object *obedit = hit ? hit->ob : objects[ob_index];
2500
2501 BMFace *efa;
2502 BMLoop *l;
2503 BMIter iter, liter;
2504 UvMapVert *vlist, *iterv, *startv;
2505 int i, stacksize = 0, *stack;
2506 uint a;
2507 char *flag;
2508 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2509
2510 if (uv_select_sync) {
2512 }
2513 else {
2515 }
2516 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2517
2518 BM_mesh_elem_table_ensure(bm, BM_FACE); /* we can use this too */
2519
2520 /* NOTE: we had 'use winding' so we don't consider overlapping islands as connected, see #44320
2521 * this made *every* projection split the island into front/back islands.
2522 * Keep 'use_winding' to false, see: #50970.
2523 *
2524 * Better solve this by having a delimit option for select-linked operator,
2525 * keeping island-select working as is. */
2526 UvVertMap *vmap = BM_uv_vert_map_create(bm, !uv_select_sync, true);
2527 if (vmap == nullptr) {
2528 continue;
2529 }
2530
2531 stack = MEM_malloc_arrayN<int>(bm->totface + 1, "UvLinkStack");
2532 flag = MEM_calloc_arrayN<char>(bm->totface, "UvLinkFlag");
2533
2534 if (hit == nullptr) {
2535 /* Use existing selection */
2536 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
2537 if (uvedit_face_visible_test(scene, efa)) {
2538 if (select_faces) {
2539 if (BM_elem_flag_test(efa, hflag)) {
2540 stack[stacksize] = a;
2541 stacksize++;
2542 flag[a] = 1;
2543 }
2544 }
2545 else {
2546 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2547 if (uvedit_uv_select_test(scene, bm, l, offsets)) {
2548 bool add_to_stack = true;
2549 if (uv_select_sync) {
2550 /* Special case, vertex/edge & sync select being enabled.
2551 *
2552 * Without this, a second linked select will 'grow' each time as each new
2553 * selection reaches the boundaries of islands that share vertices but not UVs.
2554 *
2555 * Rules applied here:
2556 * - This loops face isn't selected.
2557 * - The only other fully selected face is connected or,
2558 * - There are no connected fully selected faces UV-connected to this loop.
2559 */
2560 BLI_assert(!select_faces);
2561 if (uvedit_face_select_test(scene, bm, l->f)) {
2562 /* pass */
2563 }
2564 else {
2565 BMIter liter_other;
2566 BMLoop *l_other;
2567 BM_ITER_ELEM (l_other, &liter_other, l->v, BM_LOOPS_OF_VERT) {
2568 if ((l != l_other) && !BM_loop_uv_share_vert_check(l, l_other, offsets.uv) &&
2569 uvedit_face_select_test(scene, bm, l_other->f))
2570 {
2571 add_to_stack = false;
2572 break;
2573 }
2574 }
2575 }
2576 }
2577
2578 if (add_to_stack) {
2579 stack[stacksize] = a;
2580 stacksize++;
2581 flag[a] = 1;
2582 break;
2583 }
2584 }
2585 }
2586 }
2587 }
2588 }
2589 }
2590 else {
2591 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
2592 if (efa == hit->efa) {
2593 stack[stacksize] = a;
2594 stacksize++;
2595 flag[a] = 1;
2596 break;
2597 }
2598 }
2599 }
2600
2601 while (stacksize > 0) {
2602
2603 stacksize--;
2604 a = stack[stacksize];
2605
2606 efa = BM_face_at_index(bm, a);
2607
2608 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
2609
2610 /* make_uv_vert_map_EM sets verts tmp.l to the indices */
2611 vlist = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
2612
2613 startv = vlist;
2614
2615 for (iterv = vlist; iterv; iterv = iterv->next) {
2616 if (iterv->separate) {
2617 startv = iterv;
2618 }
2619 if (iterv->face_index == a) {
2620 break;
2621 }
2622 }
2623
2624 for (iterv = startv; iterv; iterv = iterv->next) {
2625 if ((startv != iterv) && (iterv->separate)) {
2626 break;
2627 }
2628 if (!flag[iterv->face_index]) {
2629 flag[iterv->face_index] = 1;
2630 stack[stacksize] = iterv->face_index;
2631 stacksize++;
2632 }
2633 }
2634 }
2635 }
2636
2637 /* Toggling - if any of the linked vertices is selected (and visible), we deselect. */
2638 if ((toggle == true) && (extend == false) && (deselect == false)) {
2639 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
2640 bool found_selected = false;
2641 if (!flag[a]) {
2642 continue;
2643 }
2644
2645 if (select_faces) {
2646 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
2647 if (BM_elem_flag_test(efa, hflag)) {
2648 found_selected = true;
2649 }
2650 }
2651 }
2652 else {
2653 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2654 if (uvedit_uv_select_test(scene, bm, l, offsets)) {
2655 found_selected = true;
2656 break;
2657 }
2658 }
2659
2660 if (found_selected) {
2661 deselect = true;
2662 break;
2663 }
2664 }
2665 }
2666 }
2667
2668#define SET_SELECTION(value) \
2669 if (select_faces) { \
2670 if (hflag == BM_ELEM_SELECT) { \
2671 BM_face_select_set(bm, efa, value); \
2672 } \
2673 else { \
2674 BM_elem_flag_set(efa, hflag, value); \
2675 } \
2676 } \
2677 else { \
2678 uvedit_face_select_set(scene, bm, efa, value); \
2679 } \
2680 (void)0
2681
2682 /* When sync-select is enabled in vertex or edge selection modes,
2683 * selecting an islands faces may select vertices or edges on other UV islands.
2684 * In this case it's important perform selection in two passes,
2685 * otherwise the final vertex/edge selection around UV island boundaries
2686 * will contain a mixed selection depending on the order of faces. */
2687 const bool needs_multi_pass = uv_select_sync &&
2688 (scene->toolsettings->selectmode &
2690 (deselect == false);
2691 const bool deselect_elem = !extend && !deselect && !toggle;
2692
2693 if (needs_multi_pass == false) {
2694 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
2695 if (!flag[a]) {
2696 if (deselect_elem) {
2697 SET_SELECTION(false);
2698 }
2699 continue;
2700 }
2701 if (deselect) {
2702 SET_SELECTION(false);
2703 }
2704 else {
2705 SET_SELECTION(true);
2706 }
2707 }
2708 }
2709 else {
2710 /* The same as the previous block, just use multiple passes.
2711 * It just so happens that multi-pass is only needed when selecting (deselect==false). */
2712 BLI_assert(deselect == false);
2713 /* Pass 1 (de-select). */
2714 if (deselect_elem) {
2715 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
2716 if (!flag[a]) {
2717 SET_SELECTION(false);
2718 }
2719 }
2720 }
2721 /* Pass 2 (select). */
2722 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
2723 if (!flag[a]) {
2724 continue;
2725 }
2726 SET_SELECTION(true);
2727 }
2728 }
2729
2730#undef SET_SELECTION
2731
2732 MEM_freeN(stack);
2733 MEM_freeN(flag);
2734 BM_uv_vert_map_free(vmap);
2735
2736 if (uv_select_sync) {
2738 if (deselect) {
2740 }
2741 else {
2742 if (!select_faces) {
2744 }
2745 }
2746 }
2747 else {
2749 if (bm->uv_select_sync_valid) {
2750 if (deselect) {
2752 }
2753 else {
2755 }
2757 }
2758 }
2759 }
2760 }
2761}
2762
2767 const Span<Object *> objects,
2768 Object *obedit,
2769 BMFace *efa,
2770 const bool deselect,
2771 const bool select_faces,
2772 const char hflag)
2773{
2774 const bool extend = true;
2775 const bool toggle = false;
2776
2777 UvNearestHit hit = {};
2778 hit.ob = obedit;
2779 hit.efa = efa;
2780 uv_select_linked_multi(scene, objects, &hit, extend, deselect, toggle, select_faces, hflag);
2781}
2782
2784 const BMesh *bm,
2785 BMVert *eve,
2786 const BMUVOffsets &offsets)
2787{
2788 BMIter liter;
2789 BMLoop *l;
2790
2791 BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
2792 if (!uvedit_face_visible_test(scene, l->f)) {
2793 continue;
2794 }
2795
2796 if (uvedit_uv_select_test(scene, bm, l, offsets)) {
2797 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
2798 return luv;
2799 }
2800 }
2801
2802 return nullptr;
2803}
2804
2806
2807/* -------------------------------------------------------------------- */
2810
2812{
2813 Scene *scene = CTX_data_scene(C);
2814 ViewLayer *view_layer = CTX_data_view_layer(C);
2815
2816 BMFace *efa;
2817 BMLoop *l;
2818 BMIter iter, liter;
2819 const ToolSettings *ts = scene->toolsettings;
2820
2822 scene, view_layer, nullptr);
2823
2824 const bool is_uv_face_selectmode = (ts->uv_flag & UV_FLAG_SELECT_SYNC) ?
2825 (ts->selectmode == SCE_SELECT_FACE) :
2827
2828 for (Object *obedit : objects) {
2829 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2830
2831 bool changed = false;
2832
2833 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2835 }
2836 else {
2838 }
2839 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2840
2841 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) && (bm->uv_select_sync_valid == false)) {
2843 if (select) {
2844 EDBM_select_more(em, true);
2845 }
2846 else {
2847 EDBM_select_less(em, true);
2848 }
2849
2850 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2851 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2852 continue;
2853 }
2854
2855 if (is_uv_face_selectmode) {
2856
2857 /* clear tags */
2859
2860 /* mark loops to be selected */
2861 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2862 if (uvedit_face_visible_test(scene, efa)) {
2863
2864 if (select) {
2865#define NEIGHBORING_FACE_IS_SEL 1
2866#define CURR_FACE_IS_UNSEL 2
2867
2868 int sel_state = 0;
2869
2870 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2871 if (uvedit_loop_vert_select_get(ts, bm, l)) {
2872 sel_state |= NEIGHBORING_FACE_IS_SEL;
2873 }
2874 else {
2875 sel_state |= CURR_FACE_IS_UNSEL;
2876 }
2877
2878 if (!uvedit_loop_edge_select_get(ts, bm, l)) {
2879 sel_state |= CURR_FACE_IS_UNSEL;
2880 }
2881
2882 /* If the current face is not selected and at least one neighboring face is
2883 * selected, then tag the current face to grow selection. */
2884 if (sel_state == (NEIGHBORING_FACE_IS_SEL | CURR_FACE_IS_UNSEL)) {
2886 changed = true;
2887 break;
2888 }
2889 }
2890
2891#undef NEIGHBORING_FACE_IS_SEL
2892#undef CURR_FACE_IS_UNSEL
2893 }
2894 else {
2895 if (!uvedit_face_select_test(scene, bm, efa)) {
2896 continue;
2897 }
2898 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2899 /* Deselect face when at least one of the surrounding faces is not selected */
2900 if (!uvedit_vert_is_all_other_faces_selected(ts, bm, l, offsets)) {
2902 changed = true;
2903 break;
2904 }
2905 }
2906 }
2907 }
2908 }
2909 }
2910 else {
2911
2912 /* clear tags */
2913 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2914 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2916 }
2917 }
2918
2919 /* mark loops to be selected */
2920 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2921 if (uvedit_face_visible_test(scene, efa)) {
2922 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2923
2924 if (uvedit_loop_vert_select_get(ts, bm, l) == select) {
2927 changed = true;
2928 }
2929 }
2930 }
2931 }
2932 }
2933
2934 if (changed) {
2935 if (is_uv_face_selectmode) {
2936 /* Select tagged faces. */
2937 uv_select_flush_from_tag_face(scene, obedit, select);
2938 }
2939 else {
2940 /* Select tagged loops. */
2941 uv_select_flush_from_tag_loop(scene, obedit, select);
2942 /* Set/unset edge flags based on selected verts. */
2943 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2944 /* Pass. */
2945 }
2946 else {
2948 }
2949 }
2950
2951 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2952 BLI_assert(bm->uv_select_sync_valid); /* Already handled. */
2953 if (select) {
2955 }
2956 else {
2958 }
2960 }
2961
2962 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2963 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2964 }
2965 }
2966
2967 return OPERATOR_FINISHED;
2968}
2969
2971{
2972 return uv_select_more_less(C, true);
2973}
2974
2976{
2977 /* identifiers */
2978 ot->name = "Select More";
2979 ot->description = "Select more UV vertices connected to initial selection";
2980 ot->idname = "UV_OT_select_more";
2981 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2982
2983 /* API callbacks. */
2984 ot->exec = uv_select_more_exec;
2986}
2987
2989{
2990 return uv_select_more_less(C, false);
2991}
2992
2994{
2995 /* identifiers */
2996 ot->name = "Select Less";
2997 ot->description = "Deselect UV vertices at the boundary of each selection region";
2998 ot->idname = "UV_OT_select_less";
2999 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3000
3001 /* API callbacks. */
3002 ot->exec = uv_select_less_exec;
3004}
3005
3007
3008/* -------------------------------------------------------------------- */
3011
3013{
3014 const ToolSettings *ts = scene->toolsettings;
3015 BMFace *efa;
3016 BMLoop *l;
3017 BMIter iter, liter;
3018
3019 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3020 return (bm->totvertsel || bm->totedgesel || bm->totfacesel);
3021 }
3022
3023 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3024 if (!uvedit_face_visible_test(scene, efa)) {
3025 continue;
3026 }
3027 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3029 return true;
3030 }
3031 }
3032 }
3033 return false;
3034}
3035
3037{
3038 bool found = false;
3039 for (Object *obedit : objects) {
3040 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3041 if (uvedit_select_is_any_selected(scene, bm)) {
3042 found = true;
3043 break;
3044 }
3045 }
3046 return found;
3047}
3048
3049static void uv_select_all(const Scene *scene, BMEditMesh *em, bool select_all)
3050{
3051 const ToolSettings *ts = scene->toolsettings;
3052 BMesh *bm = em->bm;
3053
3054 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3055 /* Clear all partial selection as there is no need for it. */
3056 bm->uv_select_sync_valid = false;
3057
3058 if (select_all) {
3060 }
3061 else {
3063 }
3064 return;
3065 }
3066
3067 BMFace *efa;
3068 BMLoop *l;
3069 BMIter iter, liter;
3070
3072
3073 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3074 if (!uvedit_face_visible_test(scene, efa)) {
3075 continue;
3076 }
3077 uvedit_face_select_set_no_sync(ts, bm, efa, select_all);
3078 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3079 uvedit_vert_select_set_no_sync(ts, bm, l, select_all);
3080 uvedit_edge_select_set_no_sync(ts, bm, l, select_all);
3081 }
3082 }
3083}
3084
3085static void uv_select_toggle_all(const Scene *scene, BMEditMesh *em)
3086{
3087 bool select_any = uvedit_select_is_any_selected(scene, em->bm);
3088 uv_select_all(scene, em, !select_any);
3089}
3090
3091static void uv_select_invert(const Scene *scene, BMEditMesh *em)
3092{
3093 const ToolSettings *ts = scene->toolsettings;
3094 BMesh *bm = em->bm;
3095
3096 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3098 bm->uv_select_sync_valid = false;
3099 }
3100 /* If selection wasn't synced, there is no need to sync. */
3101 if (bm->uv_select_sync_valid == false) {
3102 EDBM_select_swap(em);
3104 return;
3105 }
3106
3107 /* Invert */
3108 BMIter iter;
3109 BMFace *efa;
3110
3111 if (bm->selectmode & SCE_SELECT_VERTEX) {
3112 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3114 continue;
3115 }
3116 BMIter liter;
3117 BMLoop *l;
3118 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3120 }
3121 }
3122 /* Flush vertices to edges & faces. */
3124 }
3125 else if (em->selectmode & SCE_SELECT_EDGE) {
3126 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3127
3128 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3130 continue;
3131 }
3132 BMIter liter;
3133 BMLoop *l;
3134 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3135 /* No need to flush edges, as they will all be flipped. */
3137 /* Flush back afterwards. */
3139 }
3140 }
3141 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3143 continue;
3144 }
3145 bool face_select = true;
3146 BMIter liter;
3147 BMLoop *l;
3148 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3150 if (ts->uv_sticky == UV_STICKY_LOCATION) {
3151 BM_loop_vert_uvselect_set_shared(bm, l, true, offsets.uv);
3152 BM_loop_vert_uvselect_set_shared(bm, l->next, true, offsets.uv);
3153 }
3154 else {
3157 }
3158 }
3159 else {
3160 face_select = false;
3161 }
3162 }
3163 BM_face_uvselect_set_noflush(bm, efa, face_select);
3164 }
3165 /* Edges are flushed to faces inline. */
3166 }
3167 else {
3168 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3170 continue;
3171 }
3173 }
3174
3175 if (ts->uv_sticky == UV_STICKY_LOCATION) {
3176 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3178 }
3179 }
3180
3181 /* NOTE: no need to run: #BM_mesh_uvselect_flush_shared_only_select
3182 * because inverting doesn't change the sticky state. */
3184 return;
3185 }
3186
3188 BMFace *efa;
3189 BMLoop *l;
3190 BMIter iter, liter;
3191 char uv_selectmode = ts->uv_selectmode;
3192 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3193 if (!uvedit_face_visible_test(scene, efa)) {
3194 continue;
3195 }
3196 bool select_all = true;
3197 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3198 if (uv_selectmode & (UV_SELECT_EDGE | UV_SELECT_FACE)) {
3199 /* Use UV edge selection to find vertices and edges that must be selected. */
3200 bool es = !uvedit_edge_select_get_no_sync(ts, bm, l);
3202 uvedit_vert_select_set_no_sync(ts, bm, l, false);
3203 if (es == false) {
3204 select_all = false;
3205 }
3206 }
3207 /* Use UV vertex selection to find vertices and edges that must be selected. */
3208 else {
3209 BLI_assert(uv_selectmode & UV_SELECT_VERT);
3210 bool vs = !uvedit_vert_select_get_no_sync(ts, bm, l);
3212 uvedit_edge_select_set_no_sync(ts, bm, l, false);
3213 if (vs == false) {
3214 select_all = false;
3215 }
3216 }
3217 }
3218 uvedit_face_select_set_no_sync(ts, bm, efa, select_all);
3219 }
3220
3221 /* Flush based on uv vert/edge flags and current UV select mode */
3222 if (ELEM(uv_selectmode, UV_SELECT_EDGE, UV_SELECT_FACE)) {
3224 }
3225 else {
3226 uvedit_select_flush_from_verts(scene, bm, true);
3227 }
3228}
3229
3230void ED_uvedit_deselect_all(const Scene *scene, Object *obedit, int action)
3231{
3232 const ToolSettings *ts = scene->toolsettings;
3234
3235 /* In the case of where the selection is all or none, there is no need to hold
3236 * a separate state for UV's and the mesh. */
3237 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3238 if (em->bm->uv_select_sync_valid) {
3239 if (ELEM(action, SEL_SELECT, SEL_DESELECT)) {
3241 }
3242 }
3243 }
3244
3245 switch (action) {
3246 case SEL_TOGGLE: {
3247 uv_select_toggle_all(scene, em);
3248 break;
3249 }
3250 case SEL_SELECT: {
3251 uv_select_all(scene, em, true);
3252 break;
3253 }
3254 case SEL_DESELECT: {
3255 uv_select_all(scene, em, false);
3256 break;
3257 }
3258 case SEL_INVERT: {
3259 uv_select_invert(scene, em);
3260 break;
3261 }
3262 }
3263}
3264
3265static void uv_select_all_perform_multi_ex(const Scene *scene,
3266 const Span<Object *> objects,
3267 int action,
3268 const Object *ob_exclude)
3269{
3270 if (action == SEL_TOGGLE) {
3271 action = uvedit_select_is_any_selected_multi(scene, objects) ? SEL_DESELECT : SEL_SELECT;
3272 }
3273
3274 for (Object *obedit : objects) {
3275 if (ob_exclude && (obedit == ob_exclude)) {
3276 continue;
3277 }
3278 ED_uvedit_deselect_all(scene, obedit, action);
3279 }
3280}
3281
3282static void uv_select_all_perform_multi(const Scene *scene, Span<Object *> objects, int action)
3283{
3284 uv_select_all_perform_multi_ex(scene, objects, action, nullptr);
3285}
3286
3288{
3290 Scene *scene = CTX_data_scene(C);
3291 const ToolSettings *ts = scene->toolsettings;
3292 ViewLayer *view_layer = CTX_data_view_layer(C);
3293
3294 int action = RNA_enum_get(op->ptr, "action");
3295
3297 scene, view_layer, nullptr);
3298
3299 uv_select_all_perform_multi(scene, objects, action);
3300
3301 for (Object *obedit : objects) {
3303 }
3304
3305 return OPERATOR_FINISHED;
3306}
3307
3309{
3310 /* identifiers */
3311 ot->name = "(De)select All";
3312 ot->description = "Change selection of all UV vertices";
3313 ot->idname = "UV_OT_select_all";
3314 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3315
3316 /* API callbacks. */
3317 ot->exec = uv_select_all_exec;
3318 ot->poll = ED_operator_uvedit;
3319
3321}
3322
3324
3325/* -------------------------------------------------------------------- */
3328
3330 const Span<Object *> objects,
3331 const float co[2],
3333{
3335 const ARegion *region = CTX_wm_region(C);
3336 Scene *scene = CTX_data_scene(C);
3337 const ToolSettings *ts = scene->toolsettings;
3338 UvNearestHit hit = region ? uv_nearest_hit_init_dist_px(&region->v2d, 75.0f) :
3340 int selectmode, sticky;
3341 bool found_item = false;
3342 /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
3343 int flush = 0;
3344 const bool use_select_linked = ED_uvedit_select_island_check(ts);
3345
3346 /* Penalty (in pixels) applied to elements that are already selected
3347 * so elements that aren't already selected are prioritized. */
3348 const float penalty_dist = 3.0f * U.pixelsize;
3349
3350 /* retrieve operation mode */
3351 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3352 if (ts->selectmode & SCE_SELECT_FACE) {
3353 selectmode = UV_SELECT_FACE;
3354 }
3355 else if (ts->selectmode & SCE_SELECT_EDGE) {
3356 selectmode = UV_SELECT_EDGE;
3357 }
3358 else {
3359 selectmode = UV_SELECT_VERT;
3360 }
3361
3362 sticky = UV_STICKY_DISABLE;
3363 }
3364 else {
3365 selectmode = ts->uv_selectmode;
3366 sticky = ts->uv_sticky;
3367 }
3368
3369 /* find nearest element */
3370 if (use_select_linked) {
3371 found_item = uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit);
3372
3373 if (!found_item) {
3374 /* Without this, we can be within the face of an island but too far from an edge,
3375 * see face selection comment for details. */
3376 hit.dist_sq = FLT_MAX;
3377 found_item = uv_find_nearest_face_multi_ex(scene, objects, co, &hit, true);
3378 }
3379 }
3380 else if (selectmode == UV_SELECT_VERT) {
3381 /* find vertex */
3382 found_item = uv_find_nearest_vert_multi(scene, objects, co, penalty_dist, &hit);
3383 if (found_item) {
3384 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0) {
3387 }
3388 }
3389 }
3390 else if (selectmode == UV_SELECT_EDGE) {
3391 /* find edge */
3392 found_item = uv_find_nearest_edge_multi(scene, objects, co, penalty_dist, &hit);
3393 if (found_item) {
3394 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0) {
3397 }
3398 }
3399 }
3400 else if (selectmode == UV_SELECT_FACE) {
3401 /* find face */
3402 found_item = uv_find_nearest_face_multi(scene, objects, co, &hit);
3403
3404 if (!found_item) {
3405 /* Fallback, perform a second pass without a limited threshold,
3406 * which succeeds as long as the cursor is inside the UV face.
3407 * Useful when zoomed in, to select faces with distant screen-space face centers. */
3408 hit.dist_sq = FLT_MAX;
3409 found_item = uv_find_nearest_face_multi_ex(scene, objects, co, &hit, true);
3410 }
3411
3412 if (found_item) {
3415 }
3416 }
3417
3418 bool found = found_item;
3419 bool changed = false;
3420
3421 bool is_selected = false;
3422 if (found) {
3423 Object *obedit = hit.ob;
3424 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3425 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3426 /* Pass. */
3427 }
3428 else {
3430 }
3431 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3432
3433 if (selectmode == UV_SELECT_FACE) {
3434 is_selected = uvedit_face_select_test(scene, bm, hit.efa);
3435 }
3436 else if (selectmode == UV_SELECT_EDGE) {
3437 is_selected = uvedit_edge_select_test(scene, bm, hit.l, offsets);
3438 }
3439 else {
3440 /* Vertex or island. For island (if we were using #uv_find_nearest_face_multi_ex, see above),
3441 * `hit.l` is null, use `hit.efa` instead. */
3442 if (hit.l != nullptr) {
3443 is_selected = uvedit_uv_select_test(scene, bm, hit.l, offsets);
3444 }
3445 else {
3446 is_selected = uvedit_face_select_test(scene, bm, hit.efa);
3447 }
3448 }
3449 }
3450
3451 if (params.sel_op == SEL_OP_SET) {
3452 if ((found && params.select_passthrough) && is_selected) {
3453 found = false;
3454 }
3455 else if (found || params.deselect_all) {
3456 /* Deselect everything. */
3458 for (Object *obedit : objects) {
3460 }
3461 changed = true;
3462 }
3463 }
3464
3465 if (found) {
3466 Object *obedit = hit.ob;
3467 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3468
3469 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3470 /* Pass. */
3471 }
3472 else {
3474 }
3475 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3476 BMElem *ele_active = nullptr;
3477
3478 if (use_select_linked) {
3479 const bool extend = params.sel_op == SEL_OP_ADD;
3480 const bool deselect = params.sel_op == SEL_OP_SUB;
3481 const bool toggle = params.sel_op == SEL_OP_XOR;
3482 /* Current behavior of 'extend'
3483 * is actually toggling, so pass extend flag as 'toggle' here */
3485 scene, objects, &hit, extend, deselect, toggle, false, BM_ELEM_SELECT);
3486 /* TODO: check if this actually changed. */
3487 changed = true;
3488 }
3489 else {
3491 bool select_value = false;
3492 switch (params.sel_op) {
3493 case SEL_OP_ADD: {
3494 select_value = true;
3495 break;
3496 }
3497 case SEL_OP_SUB: {
3498 select_value = false;
3499 break;
3500 }
3501 case SEL_OP_XOR: {
3502 select_value = !is_selected;
3503 break;
3504 }
3505 case SEL_OP_SET: {
3506 /* Deselect has already been performed. */
3507 select_value = true;
3508 break;
3509 }
3510 case SEL_OP_AND: {
3511 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
3512 break;
3513 }
3514 }
3515
3516 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3518 }
3519
3520 if (selectmode == UV_SELECT_FACE) {
3521 uvedit_face_select_set_with_sticky(scene, bm, hit.efa, select_value, offsets);
3522 flush = 1;
3523 }
3524 else if (selectmode == UV_SELECT_EDGE) {
3525 uvedit_edge_select_set_with_sticky(scene, bm, hit.l, select_value, offsets);
3526 flush = 1;
3527 }
3528 else if (selectmode == UV_SELECT_VERT) {
3529 uvedit_uv_select_set_with_sticky(scene, bm, hit.l, select_value, offsets);
3530 flush = 1;
3531 }
3532 else {
3534 }
3535
3536 /* De-selecting an edge may deselect a face too - validate. */
3537 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3538 if (select_value) {
3539 /* Postpone setting active until it's known if the underlying element is selected. */
3540 if (selectmode == UV_SELECT_FACE) {
3541 ele_active = (BMElem *)hit.efa;
3542 }
3543 else if (selectmode == UV_SELECT_EDGE) {
3544 ele_active = (BMElem *)hit.l->e;
3545 }
3546 else if (selectmode == UV_SELECT_VERT) {
3547 ele_active = (BMElem *)hit.l->v;
3548 }
3549 }
3550 else {
3552 }
3553 }
3554
3555 /* (de)select sticky UV nodes. */
3556 if (sticky != UV_STICKY_DISABLE) {
3557 flush = select_value ? 1 : -1;
3558 }
3559
3560 changed = true;
3561 }
3562
3563 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3564 if (flush != 0) {
3565 if (bm->uv_select_sync_valid) {
3566 /* TODO: the picking should edge deselection to faces for e.g. */
3567
3568 /* NOTE: currently face selection handles all flushing itself.
3569 * Flushing face mode will dis-connect the shared vertices unless
3570 * shared locations are re-applied afterwards. */
3571 if (selectmode != UV_SELECT_FACE) {
3573 }
3574
3576 }
3577 else {
3579 }
3580 }
3581
3582 if (ele_active) {
3583 if (BM_elem_flag_test(ele_active, BM_ELEM_SELECT)) {
3584 BM_select_history_store(bm, ele_active);
3585 }
3586 }
3587 }
3588 else {
3589 /* Setting the selection implies a single element, which doesn't need to be flushed. */
3590 if (params.sel_op != SEL_OP_SET) {
3592 }
3593 }
3594 }
3595
3596 if (changed && found) {
3597 /* Only update the `hit` object as de-selecting all will have refreshed the others. */
3598 Object *obedit = hit.ob;
3600 }
3601
3602 return changed || found;
3603}
3604static bool uv_mouse_select(bContext *C, const float co[2], const SelectPick_Params &params)
3605{
3606 const Scene *scene = CTX_data_scene(C);
3607 ViewLayer *view_layer = CTX_data_view_layer(C);
3609 scene, view_layer, nullptr);
3610 bool changed = uv_mouse_select_multi(C, objects, co, params);
3611 return changed;
3612}
3613
3615{
3616 float co[2];
3617
3618 RNA_float_get_array(op->ptr, "location", co);
3619
3621
3622 const bool changed = uv_mouse_select(C, co, params);
3623
3624 if (changed) {
3626 }
3628}
3629
3631{
3632 const ARegion *region = CTX_wm_region(C);
3633 float co[2];
3634
3635 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
3636 RNA_float_set_array(op->ptr, "location", co);
3637
3638 const wmOperatorStatus retval = uv_select_exec(C, op);
3639
3640 return WM_operator_flag_only_pass_through_on_press(retval, event);
3641}
3642
3644{
3645 /* identifiers */
3646 ot->name = "Select";
3647 ot->description = "Select UV vertices";
3648 ot->idname = "UV_OT_select";
3649 ot->flag = OPTYPE_UNDO;
3650
3651 /* API callbacks. */
3652 ot->exec = uv_select_exec;
3653 ot->invoke = uv_select_invoke;
3654 ot->poll = ED_operator_uvedit; /* requires space image */
3655 ot->get_name = ED_select_pick_get_name;
3656
3657 /* properties */
3658 PropertyRNA *prop;
3659
3661
3662 prop = RNA_def_float_vector(
3663 ot->srna,
3664 "location",
3665 2,
3666 nullptr,
3667 -FLT_MAX,
3668 FLT_MAX,
3669 "Location",
3670 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
3671 -100.0f,
3672 100.0f);
3674}
3675
3677
3678/* -------------------------------------------------------------------- */
3681
3686
3688 const Span<Object *> objects,
3689 const float co[2],
3690 const bool extend,
3691 enum eUVLoopGenericType loop_type)
3692{
3693 const ARegion *region = CTX_wm_region(C);
3695 Scene *scene = CTX_data_scene(C);
3696 const ToolSettings *ts = scene->toolsettings;
3697 UvNearestHit hit = region ? uv_nearest_hit_init_max(&region->v2d) :
3699 bool found_item = false;
3700 /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
3701 int flush = 0;
3702
3703 /* Find edge. */
3704 found_item = uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit);
3705 if (!found_item) {
3706 return OPERATOR_CANCELLED;
3707 }
3708
3709 Object *obedit = hit.ob;
3710 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3711
3712 /* Do selection. */
3713 if (!extend) {
3714 uv_select_all_perform_multi_ex(scene, objects, SEL_DESELECT, obedit);
3715 }
3716
3717 if (loop_type == UV_LOOP_SELECT) {
3719 flush = uv_select_faceloop(scene, obedit, &hit, extend);
3720 }
3721 else {
3722 flush = uv_select_edgeloop(scene, obedit, &hit, extend);
3723 }
3724 }
3725 else if (loop_type == UV_RING_SELECT) {
3726 flush = uv_select_edgering(scene, obedit, &hit, extend);
3727 }
3728 else {
3730 }
3731
3732 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
3734 if (flush != 0) {
3735 BM_mesh_select_flush_from_verts(bm, flush == 1 ? true : false);
3736 }
3737 }
3738 else {
3739 if (flush != 0) {
3740 ED_uvedit_select_sync_flush(ts, bm, flush == 1 ? true : false);
3741 }
3742 }
3743 }
3744 else {
3746 }
3747
3748 for (Object *ob : objects) {
3750 }
3751
3753}
3755 const float co[2],
3756 const bool extend,
3757 enum eUVLoopGenericType loop_type)
3758{
3759 const Scene *scene = CTX_data_scene(C);
3760 ViewLayer *view_layer = CTX_data_view_layer(C);
3762 scene, view_layer, nullptr);
3763 wmOperatorStatus ret = uv_mouse_select_loop_generic_multi(C, objects, co, extend, loop_type);
3764 return ret;
3765}
3766
3768
3769/* -------------------------------------------------------------------- */
3772
3774{
3775 float co[2];
3776
3777 RNA_float_get_array(op->ptr, "location", co);
3778 const bool extend = RNA_boolean_get(op->ptr, "extend");
3779
3780 return uv_mouse_select_loop_generic(C, co, extend, UV_LOOP_SELECT);
3781}
3782
3784{
3785 const ARegion *region = CTX_wm_region(C);
3786 float co[2];
3787
3788 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
3789 RNA_float_set_array(op->ptr, "location", co);
3790
3791 const wmOperatorStatus retval = uv_select_loop_exec(C, op);
3792
3793 return WM_operator_flag_only_pass_through_on_press(retval, event);
3794}
3795
3797{
3798 /* identifiers */
3799 ot->name = "Loop Select";
3800 ot->description = "Select a loop of connected UV vertices";
3801 ot->idname = "UV_OT_select_loop";
3802 ot->flag = OPTYPE_UNDO;
3803
3804 /* API callbacks. */
3805 ot->exec = uv_select_loop_exec;
3806 ot->invoke = uv_select_loop_invoke;
3807 ot->poll = ED_operator_uvedit; /* requires space image */
3808
3809 /* properties */
3810 PropertyRNA *prop;
3811 prop = RNA_def_boolean(ot->srna,
3812 "extend",
3813 false,
3814 "Extend",
3815 "Extend selection rather than clearing the existing selection");
3817 prop = RNA_def_float_vector(
3818 ot->srna,
3819 "location",
3820 2,
3821 nullptr,
3822 -FLT_MAX,
3823 FLT_MAX,
3824 "Location",
3825 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
3826 -100.0f,
3827 100.0f);
3829}
3830
3832
3833/* -------------------------------------------------------------------- */
3836
3838{
3839 float co[2];
3840 RNA_float_get_array(op->ptr, "location", co);
3841 const bool extend = RNA_boolean_get(op->ptr, "extend");
3842 return uv_mouse_select_loop_generic(C, co, extend, UV_RING_SELECT);
3843}
3844
3846 wmOperator *op,
3847 const wmEvent *event)
3848{
3849 const ARegion *region = CTX_wm_region(C);
3850 float co[2];
3851
3852 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
3853 RNA_float_set_array(op->ptr, "location", co);
3854
3855 const wmOperatorStatus retval = uv_select_edge_ring_exec(C, op);
3856
3857 return WM_operator_flag_only_pass_through_on_press(retval, event);
3858}
3859
3861{
3862 /* identifiers */
3863 ot->name = "Edge Ring Select";
3864 ot->description = "Select an edge ring of connected UV vertices";
3865 ot->idname = "UV_OT_select_edge_ring";
3866 ot->flag = OPTYPE_UNDO;
3867
3868 /* API callbacks. */
3871 ot->poll = ED_operator_uvedit; /* requires space image */
3872
3873 /* properties */
3874 PropertyRNA *prop;
3875 prop = RNA_def_boolean(ot->srna,
3876 "extend",
3877 false,
3878 "Extend",
3879 "Extend selection rather than clearing the existing selection");
3881 prop = RNA_def_float_vector(
3882 ot->srna,
3883 "location",
3884 2,
3885 nullptr,
3886 -FLT_MAX,
3887 FLT_MAX,
3888 "Location",
3889 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
3890 -100.0f,
3891 100.0f);
3893}
3894
3896
3897/* -------------------------------------------------------------------- */
3900
3902 wmOperator *op,
3903 const wmEvent *event,
3904 bool pick)
3905{
3906 const ARegion *region = CTX_wm_region(C);
3907 Scene *scene = CTX_data_scene(C);
3908 const ToolSettings *ts = scene->toolsettings;
3909 ViewLayer *view_layer = CTX_data_view_layer(C);
3910 bool extend = true;
3911 bool deselect = false;
3912 bool select_faces = (ts->uv_flag & UV_FLAG_SELECT_SYNC) && (ts->selectmode & SCE_SELECT_FACE) &&
3913 (ts->uv_sticky == UV_STICKY_VERT);
3914
3915 UvNearestHit hit = region ? uv_nearest_hit_init_max(&region->v2d) :
3917
3918 if (pick) {
3919 extend = RNA_boolean_get(op->ptr, "extend");
3920 deselect = RNA_boolean_get(op->ptr, "deselect");
3921 }
3922
3924 scene, view_layer, nullptr);
3925
3926 if (pick) {
3927 float co[2];
3928
3929 if (event) {
3930 /* invoke */
3931 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
3932 RNA_float_set_array(op->ptr, "location", co);
3933 }
3934 else {
3935 /* exec */
3936 RNA_float_get_array(op->ptr, "location", co);
3937 }
3938
3939 if (!uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit)) {
3940 return OPERATOR_CANCELLED;
3941 }
3942 }
3943
3944 if (!extend && !deselect) {
3946 }
3947
3949 objects,
3950 pick ? &hit : nullptr,
3951 extend,
3952 deselect,
3953 false,
3954 select_faces,
3956
3957 if (pick) {
3960 }
3961 else {
3962 for (Object *obedit : objects) {
3963 DEG_id_tag_update(static_cast<ID *>(obedit->data),
3965 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3966 }
3967 }
3968
3969 return OPERATOR_FINISHED;
3970}
3971
3973{
3974 return uv_select_linked_internal(C, op, nullptr, false);
3975}
3976
3978{
3979 /* identifiers */
3980 ot->name = "Select Linked";
3981 ot->description = "Select all UV vertices linked to the active UV map";
3982 ot->idname = "UV_OT_select_linked";
3983
3984 /* API callbacks. */
3985 ot->exec = uv_select_linked_exec;
3986 ot->poll = ED_operator_uvedit; /* requires space image */
3987
3988 /* flags */
3989 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3990}
3991
3993
3994/* -------------------------------------------------------------------- */
3997
3999 wmOperator *op,
4000 const wmEvent *event)
4001{
4002 return uv_select_linked_internal(C, op, event, true);
4003}
4004
4006{
4007 return uv_select_linked_internal(C, op, nullptr, true);
4008}
4009
4011{
4012 /* identifiers */
4013 ot->name = "Select Linked Pick";
4014 ot->description = "Select all UV vertices linked under the mouse";
4015 ot->idname = "UV_OT_select_linked_pick";
4016
4017 /* flags */
4018 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4019
4020 /* API callbacks. */
4023 ot->poll = ED_operator_uvedit; /* requires space image */
4024
4025 /* properties */
4026 PropertyRNA *prop;
4027 prop = RNA_def_boolean(ot->srna,
4028 "extend",
4029 false,
4030 "Extend",
4031 "Extend selection rather than clearing the existing selection");
4033 prop = RNA_def_boolean(ot->srna,
4034 "deselect",
4035 false,
4036 "Deselect",
4037 "Deselect linked UV vertices rather than selecting them");
4039 prop = RNA_def_float_vector(
4040 ot->srna,
4041 "location",
4042 2,
4043 nullptr,
4044 -FLT_MAX,
4045 FLT_MAX,
4046 "Location",
4047 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
4048 -100.0f,
4049 100.0f);
4051}
4052
4054
4055/* -------------------------------------------------------------------- */
4058
4066{
4068 Scene *scene = CTX_data_scene(C);
4069 ViewLayer *view_layer = CTX_data_view_layer(C);
4070 const ToolSettings *ts = scene->toolsettings;
4071
4072 BMFace *efa;
4073 BMLoop *l;
4074 BMIter iter, liter;
4075
4076 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4077 /* Face selection. */
4078 if (ts->uv_sticky == UV_STICKY_VERT) {
4079 BKE_report(
4080 op->reports,
4081 RPT_ERROR,
4082 "Cannot split selection with \"Sync Select\" and \"Shared Vertex\" selection enabled");
4083 return OPERATOR_CANCELLED;
4084 }
4085 }
4086
4087 bool changed_multi = false;
4088
4090 scene, view_layer, nullptr);
4091
4092 for (Object *obedit : objects) {
4093 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4094
4095 bool changed = false;
4096
4097 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4099 }
4100 else {
4102 }
4103
4104 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4105 bool is_sel = false;
4106 bool is_unsel = false;
4107
4108 if (!uvedit_face_visible_test(scene, efa)) {
4109 continue;
4110 }
4111
4112 /* are we all selected? */
4113 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4114 const bool select_vert = uvedit_loop_vert_select_get(ts, bm, l);
4115 const bool select_edge = uvedit_loop_edge_select_get(ts, bm, l);
4116
4117 if (select_vert || select_edge) {
4118 is_sel = true;
4119 }
4120 if (!select_vert || !select_edge) {
4121 is_unsel = true;
4122 }
4123
4124 /* we have mixed selection, bail out */
4125 if (is_sel && is_unsel) {
4126 break;
4127 }
4128 }
4129
4130 if (is_sel && is_unsel) {
4131 /* No need to deselect the face (with sync-select) as it wont be selected,
4132 * since it already has a mixed selection. */
4133 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4134 uvedit_loop_vert_select_set(ts, bm, l, false);
4135 uvedit_loop_edge_select_set(ts, bm, l, false);
4136 }
4137
4138 changed = true;
4139 }
4140 }
4141
4142 if (changed) {
4143 changed_multi = true;
4146 }
4147 }
4148
4149 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
4150}
4151
4153{
4154 /* identifiers */
4155 ot->name = "Select Split";
4156 ot->description = "Select only entirely selected faces";
4157 ot->idname = "UV_OT_select_split";
4158 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4159
4160 /* API callbacks. */
4161 ot->exec = uv_select_split_exec;
4162 ot->poll = ED_operator_uvedit; /* requires space image */
4163}
4164
4166
4167/* -------------------------------------------------------------------- */
4172
4182static void uv_select_sync_update(const Scene *scene, Object *obedit)
4183{
4184 const ToolSettings *ts = scene->toolsettings;
4185 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4186 return;
4187 }
4188
4189 /* Sync selection has been disabled re-use or re-create the select-sync data. */
4190 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4191 /* May be -1, this is accounted for. */
4192 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
4193 if (bm->selectmode == ts->uv_selectmode) {
4194 if (bm->uv_select_sync_valid == false) {
4196 /* When the modes match, don't clear. */
4197 }
4198 }
4199 else {
4200 if (bm->uv_select_sync_valid) {
4201 BM_mesh_uvselect_mode_flush_update(bm, bm->selectmode, ts->uv_selectmode, cd_loop_uv_offset);
4202 }
4203 else {
4204 const short selectmode_orig = bm->selectmode;
4205 bm->selectmode = ts->uv_selectmode;
4207 bm->selectmode = selectmode_orig;
4208 }
4209 /* Always false because the mode doesn't match. */
4211 }
4212}
4213
4215 const ToolSettings *ts,
4216 Object *obedit)
4217{
4218 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4219 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4221 }
4222 else {
4223 Object *obedit_eval = DEG_get_evaluated(depsgraph, obedit);
4224 BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(obedit_eval->data),
4226 /* Only for region redraw. */
4228 }
4229}
4230
4235 const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
4236{
4237 uvedit_uv_select_set(scene, bm, l, select);
4238
4239 BMVert *v = l->v;
4240 BLI_assert(v->e);
4241 const BMEdge *e_iter, *e_first;
4242 e_iter = e_first = v->e;
4243 do {
4244 if (e_iter->l == nullptr) {
4245 continue;
4246 }
4247 BMLoop *l_first = e_iter->l;
4248 BMLoop *l_iter = l_first;
4249 do {
4250 if (!(l_iter->v == v && l_iter != l)) {
4251 continue;
4252 }
4253 if (!uvedit_face_visible_test(scene, l_iter->f)) {
4254 continue;
4255 }
4256 if (BM_loop_uv_share_vert_check(l, l_iter, offsets.uv)) {
4257 uvedit_uv_select_set(scene, bm, l_iter, select);
4258 }
4259 } while ((l_iter = l_iter->radial_next) != l_first);
4260 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
4261}
4262
4267 const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
4268{
4270 if (l->radial_next != l) {
4271 BMLoop *l_iter = l->radial_next;
4272 do {
4273 if (!uvedit_face_visible_test(scene, l_iter->f)) {
4274 continue;
4275 }
4276 if (BM_loop_uv_share_edge_check(l, l_iter, offsets.uv)) {
4277 uvedit_edge_select_set(scene, bm, l_iter, select);
4278 }
4279 } while ((l_iter = l_iter->radial_next) != l);
4280 }
4281}
4282
4293static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select)
4294{
4295 /* Selecting UV Faces with some modes requires us to change
4296 * the selection in other faces (depending on the sticky mode).
4297 *
4298 * This only needs to be done when the Mesh is not used for
4299 * selection (so for sticky modes, vertex or location based). */
4300
4301 const ToolSettings *ts = scene->toolsettings;
4302 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4303 BMFace *efa;
4304 BMLoop *l;
4305 BMIter iter, liter;
4306
4307 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4309 }
4310 else {
4312 }
4313 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4314
4315 bool use_sticky = true;
4316 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4318 /* Use the mesh selection directly. */
4319 use_sticky = false;
4320 }
4321 }
4322 if (ts->uv_sticky == UV_STICKY_DISABLE) {
4323 /* No need for sticky calculation when it's disabled. */
4324 use_sticky = false;
4325 }
4326
4327 if (use_sticky) {
4328 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4329 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
4330 continue;
4331 }
4332
4333 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4335 }
4336 else {
4338 }
4339 }
4340
4341 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4342 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
4343 continue;
4344 }
4345
4346 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4348
4349 if (select) {
4351 }
4352 else {
4353 if (!uvedit_vert_is_face_select_any_other(ts, bm, l, offsets)) {
4355 }
4356 if (!uvedit_edge_is_face_select_any_other(ts, bm, l, offsets)) {
4358 }
4359 }
4360 }
4361 }
4362 }
4363 else {
4364 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4365 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
4366 continue;
4367 }
4368 uvedit_face_select_set(scene, bm, efa, select);
4369 }
4370 }
4371}
4372
4383static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select)
4384{
4385 /* Selecting UV Loops with some modes requires us to change
4386 * the selection in other faces (depending on the sticky mode).
4387 *
4388 * This only needs to be done when the Mesh is not used for
4389 * selection (so for sticky modes, vertex or location based). */
4390
4391 const ToolSettings *ts = scene->toolsettings;
4392 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4393 BMFace *efa;
4394 BMLoop *l;
4395 BMIter iter, liter;
4396
4397 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4399 }
4400 else {
4402 }
4403 const bool use_mesh_select = (ts->uv_flag & UV_FLAG_SELECT_SYNC) &&
4404 (bm->uv_select_sync_valid == false);
4405
4406 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4407
4408 if ((use_mesh_select == false) && ts->uv_sticky == UV_STICKY_VERT) {
4409 /* Tag all verts as untouched, then touch the ones that have a face center
4410 * in the loop and select all UVs that use a touched vert. */
4412
4413 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4414 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4417 }
4418 }
4419 }
4420
4421 /* now select tagged verts */
4422 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4423 bool tag_all = true;
4424 bool tag_any = false;
4425 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4426 if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
4427 uvedit_uv_select_set(scene, bm, l, select);
4428 tag_any = true;
4429 }
4430 else {
4431 tag_all = false;
4432 }
4433 }
4434 if (select) {
4435 if (tag_all && uvedit_face_visible_test(scene, efa)) {
4436 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4437 BM_face_uvselect_set_noflush(bm, efa, true);
4438 }
4439 else {
4440 uvedit_face_select_set_no_sync(ts, bm, efa, true);
4441 }
4442 }
4443 }
4444 else {
4445 if (tag_any && uvedit_face_visible_test(scene, efa)) {
4446 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4447 BM_face_uvselect_set_noflush(bm, efa, false);
4448 }
4449 else {
4450 uvedit_face_select_set_no_sync(ts, bm, efa, false);
4451 }
4452 }
4453 }
4454 }
4455 }
4456 else if ((use_mesh_select == false) && (ts->uv_sticky == UV_STICKY_LOCATION)) {
4457 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4458 bool tag_all = true;
4459 bool tag_any = false;
4460 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4463 tag_any = true;
4464 }
4465 else {
4466 tag_all = false;
4467 }
4468 }
4469 if (select) {
4470 if (tag_all && uvedit_face_visible_test(scene, efa)) {
4471 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4472 BM_face_uvselect_set_noflush(bm, efa, true);
4473 }
4474 else {
4475 uvedit_face_select_set_no_sync(ts, bm, efa, true);
4476 }
4477 }
4478 }
4479 else {
4480 if (tag_any && uvedit_face_visible_test(scene, efa)) {
4481 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4482 BM_face_uvselect_set_noflush(bm, efa, false);
4483 }
4484 else {
4485 uvedit_face_select_set_no_sync(ts, bm, efa, false);
4486 }
4487 }
4488 }
4489 }
4490 }
4491 else { /* UV_STICKY_DISABLE or ts->uv_flag & UV_FLAG_SELECT_SYNC */
4492 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4493 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4495 uvedit_uv_select_set(scene, bm, l, select);
4496 }
4497 }
4498 }
4499 }
4500}
4501
4513{
4514 const ToolSettings *ts = scene->toolsettings;
4515 BMFace *efa;
4516 BMLoop *l;
4517 BMIter iter, liter;
4518
4519 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4521 }
4522 else {
4524 }
4525 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4526
4527 bool use_sticky = true;
4528 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4529 if (ts->uv_sticky == UV_STICKY_VERT) {
4530 /* Use the mesh selection directly. */
4531 use_sticky = false;
4532 }
4533 }
4534 if (ts->uv_sticky == UV_STICKY_DISABLE) {
4535 /* No need for sticky calculation when it's disabled. */
4536 use_sticky = false;
4537 }
4538
4539 if (use_sticky) {
4540 /* Use UV edge selection to identify which verts must to be selected */
4541
4542 /* Clear UV vert flags */
4544
4545 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4546 if (!uvedit_face_visible_test(scene, efa)) {
4547 /* This visibility check could be removed? Simply relying on edge flags to ensure
4548 * visibility might be sufficient. */
4549 continue;
4550 }
4551 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4552 /* Select verts based on UV edge flag. */
4555 uvedit_uv_select_flush_from_tag_sticky_loc_internal(scene, bm, l->next, true, offsets);
4556 }
4557 }
4558 }
4559 }
4560 else {
4561 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4563 }
4564 else {
4565 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4566 bool select_all = true;
4567 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4570 uvedit_vert_select_set_no_sync(ts, bm, l->next, true);
4571 }
4572 else if (!uvedit_edge_select_get_no_sync(ts, bm, l->prev)) {
4573 uvedit_vert_select_set_no_sync(ts, bm, l->next, false);
4574 select_all = false;
4575 }
4576 }
4577 uvedit_face_select_set_no_sync(ts, bm, efa, select_all);
4578 }
4579 }
4580 }
4581}
4582
4584
4585/* -------------------------------------------------------------------- */
4588
4590{
4591 const Scene *scene = CTX_data_scene(C);
4592 const bool pinned = RNA_boolean_get(op->ptr, "pinned");
4593 if (pinned) {
4594 if (!uvedit_select_pin_ok_or_report(scene, op->reports)) {
4595 return OPERATOR_CANCELLED;
4596 }
4597 }
4598 return WM_gesture_box_invoke(C, op, event);
4599}
4600
4602{
4603 const Scene *scene = CTX_data_scene(C);
4604 const ToolSettings *ts = scene->toolsettings;
4605
4606 const bool pinned = RNA_boolean_get(op->ptr, "pinned");
4607
4608 /* Note that face selection uses the face-center. */
4609 const char uv_select_mode = ED_uvedit_select_mode_get(scene);
4610 const bool use_select_linked = pinned ? false : ED_uvedit_select_island_check(ts);
4611
4612 if (pinned) {
4613 if (!uvedit_select_pin_ok_or_report(scene, op->reports)) {
4614 return OPERATOR_CANCELLED;
4615 }
4616 }
4617
4619 ViewLayer *view_layer = CTX_data_view_layer(C);
4620 const ARegion *region = CTX_wm_region(C);
4621 BMFace *efa;
4622 BMLoop *l;
4623 BMIter iter, liter;
4624 float *luv;
4625 rctf rectf;
4626
4627 /* get rectangle from operator */
4629 UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
4630
4631 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
4632 const bool select = (sel_op != SEL_OP_SUB);
4633 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
4634
4635 bool changed_multi = false;
4636
4638 scene, view_layer, nullptr);
4639
4640 if (use_pre_deselect) {
4642 }
4643
4644 /* don't indent to avoid diff noise! */
4645 for (Object *obedit : objects) {
4646 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4647
4648 bool changed = false;
4649
4650 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4652 }
4653 else {
4655 }
4656
4657 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4658
4659 /* do actual selection */
4660 if (pinned && offsets.pin == -1) {
4661 /* Special case, nothing is pinned so it's known in advance that nothing will be selected.
4662 * Still run the code after this block finishes as the UV's may have been de-selected. */
4663 }
4664 else if (uv_select_mode == UV_SELECT_FACE) {
4665 /* Handle face selection (face center). */
4666 if (use_select_linked) {
4668 }
4669 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4670 if (use_select_linked) {
4671 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
4672 continue;
4673 }
4674 }
4675 else {
4677 }
4678
4679 bool has_selected = false;
4680 if (uvedit_face_visible_test(scene, efa)) {
4681 float cent[2];
4682 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
4683 if (BLI_rctf_isect_pt_v(&rectf, cent)) {
4685 has_selected = true;
4686 changed = true;
4687 }
4688 }
4689 if (has_selected && use_select_linked) {
4691 scene, objects, obedit, efa, !select, true, BM_ELEM_TAG);
4692 }
4693 }
4694
4695 /* (de)selects all tagged faces and deals with sticky modes */
4696 if (changed) {
4697 uv_select_flush_from_tag_face(scene, obedit, select);
4698 }
4699 }
4700 else if (uv_select_mode == UV_SELECT_EDGE) {
4701 bool do_second_pass = true;
4702 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4703 if (!uvedit_face_visible_test(scene, efa)) {
4704 continue;
4705 }
4706
4707 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
4708 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
4709
4710 bool has_selected = false;
4711 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4712 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4713 if (BLI_rctf_isect_pt_v(&rectf, luv) && BLI_rctf_isect_pt_v(&rectf, luv_prev)) {
4714 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
4715 do_second_pass = false;
4716 has_selected = true;
4717 changed = true;
4718 }
4719 l_prev = l;
4720 luv_prev = luv;
4721 }
4722 if (has_selected && use_select_linked) {
4724 scene, objects, obedit, efa, !select, false, BM_ELEM_SELECT);
4725 }
4726 }
4727 /* Do a second pass if no complete edges could be selected.
4728 * This matches wire-frame edit-mesh selection in the 3D view. */
4729 if (do_second_pass) {
4730 /* Second pass to check if edges partially overlap with the selection area (box). */
4731 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4732 if (!uvedit_face_visible_test(scene, efa)) {
4733 continue;
4734 }
4735 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
4736 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
4737
4738 bool has_selected = false;
4739 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4740 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4741 if (BLI_rctf_isect_segment(&rectf, luv_prev, luv)) {
4742 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
4743 has_selected = true;
4744 changed = true;
4745 }
4746 l_prev = l;
4747 luv_prev = luv;
4748 }
4749 if (has_selected && use_select_linked) {
4751 scene, objects, obedit, efa, !select, false, BM_ELEM_SELECT);
4752 }
4753 }
4754 }
4755 }
4756 else {
4757 /* Handle vert selection. */
4758 BLI_assert(uv_select_mode == UV_SELECT_VERT);
4759
4760 changed = true;
4762
4763 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4764 if (!uvedit_face_visible_test(scene, efa)) {
4765 continue;
4766 }
4767 bool has_selected = false;
4768 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4769 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4770 if (select != uvedit_uv_select_test(scene, bm, l, offsets)) {
4771 if (BLI_rctf_isect_pt_v(&rectf, luv)) {
4772 if (!pinned || BM_ELEM_CD_GET_BOOL(l, offsets.pin)) {
4773 uvedit_uv_select_set(scene, bm, l, select);
4775 has_selected = true;
4776 }
4777 }
4778 }
4779 }
4780 if (has_selected && use_select_linked) {
4782 scene, objects, obedit, efa, !select, false, BM_ELEM_SELECT);
4783 }
4784 }
4785
4786 if (ts->uv_sticky == UV_STICKY_VERT) {
4788 }
4789 }
4790
4791 if (changed || use_pre_deselect) {
4792 changed_multi = true;
4793 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4795 }
4796 else {
4798 }
4800 }
4801 }
4802
4803 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
4804}
4805
4807{
4808 /* identifiers */
4809 ot->name = "Box Select";
4810 ot->description = "Select UV vertices using box selection";
4811 ot->idname = "UV_OT_select_box";
4812
4813 /* API callbacks. */
4814 ot->invoke = uv_box_select_invoke;
4815 ot->exec = uv_box_select_exec;
4816 ot->modal = WM_gesture_box_modal;
4817 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
4818 ot->cancel = WM_gesture_box_cancel;
4819
4820 /* flags */
4821 ot->flag = OPTYPE_UNDO;
4822
4823 /* properties */
4824 RNA_def_boolean(ot->srna, "pinned", false, "Pinned", "Border select pinned UVs only");
4825
4828}
4829
4831
4832/* -------------------------------------------------------------------- */
4835
4836static bool uv_circle_select_is_point_inside(const float uv[2],
4837 const float offset[2],
4838 const float ellipse[2])
4839{
4840 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
4841 const float co[2] = {
4842 (uv[0] - offset[0]) * ellipse[0],
4843 (uv[1] - offset[1]) * ellipse[1],
4844 };
4845 return len_squared_v2(co) < 1.0f;
4846}
4847
4848static bool uv_circle_select_is_edge_inside(const float uv_a[2],
4849 const float uv_b[2],
4850 const float offset[2],
4851 const float ellipse[2])
4852{
4853 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
4854 const float co_a[2] = {
4855 (uv_a[0] - offset[0]) * ellipse[0],
4856 (uv_a[1] - offset[1]) * ellipse[1],
4857 };
4858 const float co_b[2] = {
4859 (uv_b[0] - offset[0]) * ellipse[0],
4860 (uv_b[1] - offset[1]) * ellipse[1],
4861 };
4862 const float co_zero[2] = {0.0f, 0.0f};
4863 return dist_squared_to_line_segment_v2(co_zero, co_a, co_b) < 1.0f;
4864}
4865
4867{
4870 Scene *scene = CTX_data_scene(C);
4871 ViewLayer *view_layer = CTX_data_view_layer(C);
4872 const ToolSettings *ts = scene->toolsettings;
4873 const ARegion *region = CTX_wm_region(C);
4874 BMFace *efa;
4875 BMLoop *l;
4876 BMIter iter, liter;
4877 float *luv;
4878 int x, y, radius, width, height;
4879 float zoomx, zoomy;
4880 float offset[2], ellipse[2];
4881
4882 /* Note that face selection uses the face-center. */
4883 const char uv_select_mode = ED_uvedit_select_mode_get(scene);
4884 const bool use_select_linked = ED_uvedit_select_island_check(ts);
4885
4886 /* get operator properties */
4887 x = RNA_int_get(op->ptr, "x");
4888 y = RNA_int_get(op->ptr, "y");
4889 radius = RNA_int_get(op->ptr, "radius");
4890
4891 /* compute ellipse size and location, not a circle since we deal
4892 * with non square image. ellipse is normalized, r = 1.0. */
4893 ED_space_image_get_size(sima, &width, &height);
4894 ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
4895
4896 ellipse[0] = width * zoomx / radius;
4897 ellipse[1] = height * zoomy / radius;
4898
4899 UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
4900
4901 bool changed_multi = false;
4902
4904 scene, view_layer, nullptr);
4905
4906 const eSelectOp sel_op = ED_select_op_modal(
4907 eSelectOp(RNA_enum_get(op->ptr, "mode")),
4908 WM_gesture_is_modal_first(static_cast<wmGesture *>(op->customdata)));
4909 const bool select = (sel_op != SEL_OP_SUB);
4910 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
4911
4912 if (use_pre_deselect) {
4914 }
4915
4916 for (Object *obedit : objects) {
4917 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4918
4919 bool changed = false;
4920
4921 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
4923 }
4924 else {
4926 }
4927 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4928
4929 /* do selection */
4930 if (uv_select_mode == UV_SELECT_FACE) {
4931 /* Handle face selection (face center). */
4932 if (use_select_linked) {
4934 }
4935 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4936 if (use_select_linked) {
4937 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
4938 continue;
4939 }
4940 }
4941 else {
4943 if (select == uvedit_face_select_test(scene, bm, efa)) {
4944 continue;
4945 }
4946 }
4947
4948 bool has_selected = false;
4949 float cent[2];
4950 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
4951 if (uv_circle_select_is_point_inside(cent, offset, ellipse)) {
4953 has_selected = true;
4954 changed = true;
4955 }
4956
4957 if (has_selected && use_select_linked) {
4959 scene, objects, obedit, efa, !select, true, BM_ELEM_TAG);
4960 }
4961 }
4962
4963 /* (de)selects all tagged faces and deals with sticky modes */
4964 if (changed) {
4965 uv_select_flush_from_tag_face(scene, obedit, select);
4966 }
4967 }
4968 else if (uv_select_mode == UV_SELECT_EDGE) {
4969 /* Handle edge selection. */
4970 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4971 if (!uvedit_face_visible_test(scene, efa)) {
4972 continue;
4973 }
4974
4975 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
4976 const float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
4977
4978 bool has_selected = false;
4979 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4980 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4981 if (uv_circle_select_is_edge_inside(luv, luv_prev, offset, ellipse)) {
4982 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
4983 has_selected = true;
4984 changed = true;
4985 }
4986 l_prev = l;
4987 luv_prev = luv;
4988 }
4989 if (has_selected && use_select_linked) {
4991 scene, objects, obedit, efa, !select, false, BM_ELEM_SELECT);
4992 }
4993 }
4994 }
4995 else {
4996 /* Handle vert selection. */
4997 BLI_assert(uv_select_mode == UV_SELECT_VERT);
4998
5000
5001 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5002 if (!uvedit_face_visible_test(scene, efa)) {
5003 continue;
5004 }
5005 bool has_selected = false;
5006 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5007 if (select != uvedit_uv_select_test(scene, bm, l, offsets)) {
5008 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
5009 if (uv_circle_select_is_point_inside(luv, offset, ellipse)) {
5010 changed = true;
5011 uvedit_uv_select_set(scene, bm, l, select);
5013 has_selected = true;
5014 }
5015 }
5016 }
5017 if (has_selected && use_select_linked) {
5019 scene, objects, obedit, efa, !select, false, BM_ELEM_SELECT);
5020 }
5021 }
5022
5023 if (ts->uv_sticky == UV_STICKY_VERT) {
5025 }
5026 }
5027
5028 if (changed || use_pre_deselect) {
5029 changed_multi = true;
5030 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
5032 }
5033 else {
5035 }
5037 }
5038 }
5039
5040 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
5041}
5042
5044{
5045 /* identifiers */
5046 ot->name = "Circle Select";
5047 ot->description = "Select UV vertices using circle selection";
5048 ot->idname = "UV_OT_select_circle";
5049
5050 /* API callbacks. */
5051 ot->invoke = WM_gesture_circle_invoke;
5052 ot->modal = WM_gesture_circle_modal;
5053 ot->exec = uv_circle_select_exec;
5054 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
5055 ot->cancel = WM_gesture_circle_cancel;
5056 ot->get_name = ED_select_circle_get_name;
5057
5058 /* flags */
5059 ot->flag = OPTYPE_UNDO;
5060
5061 /* properties */
5064}
5065
5067
5068/* -------------------------------------------------------------------- */
5071
5073 const rcti *clip_rect,
5074 const Span<int2> mcoords,
5075 const float co_test[2])
5076{
5077 int co_screen[2];
5079 &region->v2d, co_test[0], co_test[1], &co_screen[0], &co_screen[1]) &&
5080 BLI_rcti_isect_pt_v(clip_rect, co_screen) &&
5081 BLI_lasso_is_point_inside(mcoords, co_screen[0], co_screen[1], V2D_IS_CLIPPED))
5082 {
5083 return true;
5084 }
5085 return false;
5086}
5087
5089 const rcti *clip_rect,
5090 const Span<int2> mcoords,
5091 const float co_test_a[2],
5092 const float co_test_b[2])
5093{
5094 int co_screen_a[2], co_screen_b[2];
5096 &region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) &&
5097 BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) &&
5099 mcoords, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED))
5100 {
5101 return true;
5102 }
5103 return false;
5104}
5105
5106static bool do_lasso_select_mesh_uv(bContext *C, const Span<int2> mcoords, const eSelectOp sel_op)
5107{
5109 const ARegion *region = CTX_wm_region(C);
5110 Scene *scene = CTX_data_scene(C);
5111 const ToolSettings *ts = scene->toolsettings;
5112 ViewLayer *view_layer = CTX_data_view_layer(C);
5113
5114 /* Note that face selection uses the face-center. */
5115 const char uv_select_mode = ED_uvedit_select_mode_get(scene);
5116 const bool use_select_linked = ED_uvedit_select_island_check(ts);
5117
5118 const bool select = (sel_op != SEL_OP_SUB);
5119 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
5120
5121 BMIter iter, liter;
5122
5123 BMFace *efa;
5124 BMLoop *l;
5125 bool changed_multi = false;
5126 rcti rect;
5127
5128 BLI_lasso_boundbox(&rect, mcoords);
5129
5131 scene, view_layer, nullptr);
5132
5133 if (use_pre_deselect) {
5135 }
5136
5137 for (Object *obedit : objects) {
5138
5139 bool changed = false;
5140
5141 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
5142
5143 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
5145 }
5146 else {
5148 }
5149 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5150
5151 if (uv_select_mode == UV_SELECT_FACE) {
5152 /* Handle face selection (face center). */
5153 if (use_select_linked) {
5155 }
5156 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5157 if (use_select_linked) {
5158 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
5159 continue;
5160 }
5161 }
5162 else {
5164 if (select == uvedit_face_select_test(scene, bm, efa)) {
5165 continue;
5166 }
5167 }
5168
5169 bool has_selected = false;
5170 float cent[2];
5171 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
5172 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, cent)) {
5174 has_selected = true;
5175 changed = true;
5176 }
5177
5178 if (has_selected && use_select_linked) {
5180 scene, objects, obedit, efa, !select, true, BM_ELEM_TAG);
5181 }
5182 }
5183
5184 /* (de)selects all tagged faces and deals with sticky modes */
5185 if (changed) {
5186 uv_select_flush_from_tag_face(scene, obedit, select);
5187 }
5188 }
5189 else if (uv_select_mode == UV_SELECT_EDGE) {
5190 /* Handle edge selection. */
5191 bool do_second_pass = true;
5192 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5193 if (!uvedit_face_visible_test(scene, efa)) {
5194 continue;
5195 }
5196
5197 bool has_selected = false;
5198 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
5199 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
5200 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5201 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
5202 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv) &&
5203 do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv_prev))
5204 {
5205 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
5206 do_second_pass = false;
5207 has_selected = true;
5208 changed = true;
5209 }
5210 l_prev = l;
5211 luv_prev = luv;
5212 }
5213 if (has_selected && use_select_linked) {
5215 scene, objects, obedit, efa, !select, false, BM_ELEM_SELECT);
5216 }
5217 }
5218 /* Do a second pass if no complete edges could be selected.
5219 * This matches wire-frame edit-mesh selection in the 3D view. */
5220 if (do_second_pass) {
5221 /* Second pass to check if edges partially overlap with the selection area (lasso). */
5222 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5223 if (!uvedit_face_visible_test(scene, efa)) {
5224 continue;
5225 }
5226 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
5227 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
5228
5229 bool has_selected = false;
5230 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5231 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
5232 if (do_lasso_select_mesh_uv_is_edge_inside(region, &rect, mcoords, luv, luv_prev)) {
5233 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
5234 has_selected = true;
5235 changed = true;
5236 }
5237 l_prev = l;
5238 luv_prev = luv;
5239 }
5240 if (has_selected && use_select_linked) {
5242 scene, objects, obedit, efa, !select, false, BM_ELEM_SELECT);
5243 }
5244 }
5245 }
5246 }
5247 else {
5248 /* Handle vert selection. */
5249 BLI_assert(uv_select_mode == UV_SELECT_VERT);
5250
5252
5253 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5254 if (!uvedit_face_visible_test(scene, efa)) {
5255 continue;
5256 }
5257 bool has_selected = false;
5258 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5259 if (select != uvedit_uv_select_test(scene, bm, l, offsets)) {
5260 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
5261 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv)) {
5262 uvedit_uv_select_set(scene, bm, l, select);
5263 changed = true;
5265 has_selected = true;
5266 }
5267 }
5268 }
5269 if (has_selected && use_select_linked) {
5271 scene, objects, obedit, efa, !select, false, BM_ELEM_SELECT);
5272 }
5273 }
5274
5275 if (ts->uv_sticky == UV_STICKY_VERT) {
5277 }
5278 }
5279
5280 if (changed || use_pre_deselect) {
5281 changed_multi = true;
5282 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
5284 }
5285 else {
5287 }
5289 }
5290 }
5291
5292 return changed_multi;
5293}
5294
5296{
5298 if (mcoords.is_empty()) {
5299 return OPERATOR_PASS_THROUGH;
5300 }
5301
5302 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
5303 bool changed = do_lasso_select_mesh_uv(C, mcoords, sel_op);
5304
5305 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
5306}
5307
5309{
5310 ot->name = "Lasso Select UV";
5311 ot->description = "Select UVs using lasso selection";
5312 ot->idname = "UV_OT_select_lasso";
5313
5314 ot->invoke = WM_gesture_lasso_invoke;
5315 ot->modal = WM_gesture_lasso_modal;
5316 ot->exec = uv_lasso_select_exec;
5318 ot->cancel = WM_gesture_lasso_cancel;
5319
5320 /* flags */
5322
5323 /* properties */
5326}
5327
5329
5330/* -------------------------------------------------------------------- */
5333
5335{
5336 const Scene *scene = CTX_data_scene(C);
5337 const ToolSettings *ts = scene->toolsettings;
5338
5339 /* Use this operator only in vertex mode, since it is not guaranteed that pinned vertices may
5340 * form higher selection states (like edges/faces/islands) in other modes. */
5341 if (!uvedit_select_pin_ok_or_report(scene, op->reports)) {
5342 return OPERATOR_CANCELLED;
5343 }
5344
5346 ViewLayer *view_layer = CTX_data_view_layer(C);
5347 BMFace *efa;
5348 BMLoop *l;
5349 BMIter iter, liter;
5350
5352 scene, view_layer, nullptr);
5353
5354 for (Object *obedit : objects) {
5355 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
5356
5357 const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2);
5358 if (!BM_uv_map_attr_pin_exists(bm, active_uv_name)) {
5359 continue;
5360 }
5361
5362 bool changed = false;
5363 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
5365 }
5366 else {
5368 }
5369 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5370
5371 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5372 if (!uvedit_face_visible_test(scene, efa)) {
5373 continue;
5374 }
5375
5376 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5377
5378 if (BM_ELEM_CD_GET_BOOL(l, offsets.pin)) {
5379 uvedit_uv_select_enable(scene, bm, l);
5380 changed = true;
5381 }
5382 }
5383 }
5384 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
5386 }
5387 else {
5389 }
5390
5391 if (changed) {
5393 }
5394 }
5395
5396 return OPERATOR_FINISHED;
5397}
5398
5400{
5401 /* identifiers */
5402 ot->name = "Selected Pinned";
5403 ot->description = "Select all pinned UV vertices";
5404 ot->idname = "UV_OT_select_pinned";
5405 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5406
5407 /* API callbacks. */
5408 ot->exec = uv_select_pinned_exec;
5409 ot->poll = ED_operator_uvedit;
5410}
5411
5413
5414/* -------------------------------------------------------------------- */
5417
5418BLI_INLINE uint overlap_hash(const void *overlap_v)
5419{
5420 const BVHTreeOverlap *overlap = static_cast<const BVHTreeOverlap *>(overlap_v);
5421
5422 /* Designed to treat (A,B) and (B,A) as the same. */
5423 int x = overlap->indexA;
5424 int y = overlap->indexB;
5425 if (x > y) {
5426 std::swap(x, y);
5427 }
5428 return BLI_hash_int_2d(x, y);
5429}
5430
5431BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v)
5432{
5433 const BVHTreeOverlap *a = static_cast<const BVHTreeOverlap *>(a_v);
5434 const BVHTreeOverlap *b = static_cast<const BVHTreeOverlap *>(b_v);
5435 return !((a->indexA == b->indexA && a->indexB == b->indexB) ||
5436 (a->indexA == b->indexB && a->indexB == b->indexA));
5437}
5438
5442 float tri[3][2];
5443};
5444
5452static bool overlap_tri_tri_uv_test(const float t1[3][2],
5453 const float t2[3][2],
5454 const float endpoint_bias)
5455{
5456 float vi[2];
5457
5458 /* Don't use 'isect_tri_tri_v2' here
5459 * because it's important to ignore overlap at end-points. */
5460 if (isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[0], t2[1], endpoint_bias, vi) == 1 ||
5461 isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[1], t2[2], endpoint_bias, vi) == 1 ||
5462 isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[2], t2[0], endpoint_bias, vi) == 1 ||
5463 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[0], t2[1], endpoint_bias, vi) == 1 ||
5464 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[1], t2[2], endpoint_bias, vi) == 1 ||
5465 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[2], t2[0], endpoint_bias, vi) == 1 ||
5466 isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[0], t2[1], endpoint_bias, vi) == 1 ||
5467 isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[1], t2[2], endpoint_bias, vi) == 1)
5468 {
5469 return true;
5470 }
5471
5472 /* When none of the segments intersect, checking if either of the triangles corners
5473 * is inside the others is almost always sufficient to test if the two triangles intersect.
5474 *
5475 * However, the `endpoint_bias` on segment intersections causes _exact_ overlapping
5476 * triangles not to be detected.
5477 *
5478 * Resolve this problem at the small cost of calculating the triangle center, see #85508. */
5479 mid_v2_v2v2v2(vi, UNPACK3(t1));
5480 if (isect_point_tri_v2(vi, UNPACK3(t2)) != 0) {
5481 return true;
5482 }
5483 mid_v2_v2v2v2(vi, UNPACK3(t2));
5484 if (isect_point_tri_v2(vi, UNPACK3(t1)) != 0) {
5485 return true;
5486 }
5487
5488 return false;
5489}
5490
5492{
5494 const Scene *scene = CTX_data_scene(C);
5495 const ToolSettings *ts = scene->toolsettings;
5496 const bool uv_select_sync = (ts->uv_flag & UV_FLAG_SELECT_SYNC);
5497 ViewLayer *view_layer = CTX_data_view_layer(C);
5498
5500 scene, view_layer, nullptr);
5501
5502 struct ChangedInfo {
5503 uint has_changed : 1;
5504 uint has_overlap : 1;
5505 };
5506
5507 Array<ChangedInfo> objects_tag(objects.size(), {false, false});
5508
5509 /* Calculate maximum number of tree nodes and prepare initial selection. */
5510 uint uv_tri_len = 0;
5511 for (const int i : blender::IndexRange(objects.size())) {
5512 Object *obedit = objects[i];
5513
5514 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
5515
5519 if (!extend) {
5520 ED_uvedit_deselect_all(scene, obedit, SEL_DESELECT);
5521 objects_tag[i].has_changed = true;
5522 }
5523
5524 BMIter iter;
5525 BMFace *efa;
5526 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5527 if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) {
5528 continue;
5529 }
5530 uv_tri_len += efa->len - 2;
5531 }
5532 }
5533
5534 UVOverlapData *overlap_data = MEM_malloc_arrayN<UVOverlapData>(uv_tri_len, "UvOverlapData");
5535 BVHTree *uv_tree = BLI_bvhtree_new(uv_tri_len, 0.0f, 4, 6);
5536
5537 /* Use a global data index when inserting into the BVH. */
5538 int data_index = 0;
5539
5540 int face_len_alloc = 3;
5541 float (*uv_verts)[2] = static_cast<float (*)[2]>(
5542 MEM_mallocN(sizeof(*uv_verts) * face_len_alloc, "UvOverlapCoords"));
5543 uint(*indices)[3] = static_cast<uint(*)[3]>(
5544 MEM_mallocN(sizeof(*indices) * (face_len_alloc - 2), "UvOverlapTris"));
5545
5548
5549 for (const int ob_index : objects.index_range()) {
5550 Object *obedit = objects[ob_index];
5551 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
5552 BMIter iter, liter;
5553 BMFace *efa;
5554 BMLoop *l;
5555
5556 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
5557
5558 /* Triangulate each UV face and store it inside the BVH. */
5559 int face_index;
5560 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, face_index) {
5561
5562 if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) {
5563 continue;
5564 }
5565
5566 const uint face_len = efa->len;
5567 const uint tri_len = face_len - 2;
5568
5569 if (face_len_alloc < face_len) {
5570 MEM_freeN(uv_verts);
5572 uv_verts = static_cast<float (*)[2]>(
5573 MEM_mallocN(sizeof(*uv_verts) * face_len, "UvOverlapCoords"));
5574 indices = static_cast<uint(*)[3]>(
5575 MEM_mallocN(sizeof(*indices) * tri_len, "UvOverlapTris"));
5576 face_len_alloc = face_len;
5577 }
5578
5579 int vert_index;
5580 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, vert_index) {
5581 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
5582 copy_v2_v2(uv_verts[vert_index], luv);
5583 }
5584
5585 /* The UV coordinates winding could be positive of negative,
5586 * determine it automatically. */
5587 const int coords_sign = 0;
5588 BLI_polyfill_calc_arena(uv_verts, face_len, coords_sign, indices, arena);
5589
5590 /* A beauty fill is necessary to remove degenerate triangles that may be produced from the
5591 * above poly-fill (see #103913), otherwise the overlap tests can fail. */
5592 BLI_polyfill_beautify(uv_verts, face_len, indices, arena, heap);
5593
5594 for (int t = 0; t < tri_len; t++) {
5595 overlap_data[data_index].ob_index = ob_index;
5596 overlap_data[data_index].face_index = face_index;
5597
5598 /* BVH needs 3D, overlap data uses 2D. */
5599 const float tri[3][3] = {
5600 {UNPACK2(uv_verts[indices[t][0]]), 0.0f},
5601 {UNPACK2(uv_verts[indices[t][1]]), 0.0f},
5602 {UNPACK2(uv_verts[indices[t][2]]), 0.0f},
5603 };
5604
5605 copy_v2_v2(overlap_data[data_index].tri[0], tri[0]);
5606 copy_v2_v2(overlap_data[data_index].tri[1], tri[1]);
5607 copy_v2_v2(overlap_data[data_index].tri[2], tri[2]);
5608
5609 BLI_bvhtree_insert(uv_tree, data_index, &tri[0][0], 3);
5610 data_index++;
5611 }
5612
5613 BLI_memarena_clear(arena);
5614 BLI_heap_clear(heap, nullptr);
5615 }
5616 }
5617 BLI_assert(data_index == uv_tri_len);
5618
5619 BLI_memarena_free(arena);
5620 BLI_heap_free(heap, nullptr);
5621 MEM_freeN(uv_verts);
5623
5624 BLI_bvhtree_balance(uv_tree);
5625
5626 uint tree_overlap_len;
5627 BVHTreeOverlap *overlap = BLI_bvhtree_overlap_self(uv_tree, &tree_overlap_len, nullptr, nullptr);
5628
5629 if (overlap != nullptr) {
5630 GSet *overlap_set = BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, tree_overlap_len);
5631
5632 for (int i = 0; i < tree_overlap_len; i++) {
5633 /* Skip overlaps against yourself. */
5634 if (overlap[i].indexA == overlap[i].indexB) {
5635 continue;
5636 }
5637
5638 /* Skip overlaps that have already been tested. */
5639 if (!BLI_gset_add(overlap_set, &overlap[i])) {
5640 continue;
5641 }
5642
5643 const UVOverlapData *o_a = &overlap_data[overlap[i].indexA];
5644 const UVOverlapData *o_b = &overlap_data[overlap[i].indexB];
5645 Object *obedit_a = objects[o_a->ob_index];
5646 Object *obedit_b = objects[o_b->ob_index];
5647 BMesh *bm_a = BKE_editmesh_from_object(obedit_a)->bm;
5648 BMesh *bm_b = BKE_editmesh_from_object(obedit_b)->bm;
5649 BMFace *face_a = bm_a->ftable[o_a->face_index];
5650 BMFace *face_b = bm_b->ftable[o_b->face_index];
5651
5652 /* Skip if both faces are already selected. */
5653 if (uvedit_face_select_test(scene, bm_a, face_a) &&
5654 uvedit_face_select_test(scene, bm_b, face_b))
5655 {
5656 continue;
5657 }
5658
5659 /* Main tri-tri overlap test. */
5660 const float endpoint_bias = -1e-4f;
5661 if (overlap_tri_tri_uv_test(o_a->tri, o_b->tri, endpoint_bias)) {
5662 objects_tag[o_a->ob_index].has_overlap = true;
5663 objects_tag[o_b->ob_index].has_overlap = true;
5666 }
5667 }
5668
5669 BLI_gset_free(overlap_set, nullptr);
5670 MEM_freeN(overlap);
5671 }
5672
5673 for (const int i : blender::IndexRange(objects.size())) {
5674 Object *obedit = objects[i];
5675 const ChangedInfo &tag_info = objects_tag[i];
5676 const bool select = true;
5677
5678 if (tag_info.has_overlap) {
5679 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
5680
5681 if (uv_select_sync) {
5683 }
5684 else {
5686 }
5687 uv_select_flush_from_tag_face(scene, obedit, select);
5688
5689 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
5691 }
5692 else {
5694 }
5695 }
5696
5697 if (tag_info.has_changed || tag_info.has_overlap) {
5699 }
5700 }
5701
5702 BLI_bvhtree_free(uv_tree);
5703
5704 MEM_freeN(overlap_data);
5705
5706 return OPERATOR_FINISHED;
5707}
5708
5710{
5711 bool extend = RNA_boolean_get(op->ptr, "extend");
5712 return uv_select_overlap(C, extend);
5713}
5714
5716{
5717 /* identifiers */
5718 ot->name = "Select Overlap";
5719 ot->description = "Select all UV faces which overlap each other";
5720 ot->idname = "UV_OT_select_overlap";
5721 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5722
5723 /* API callbacks. */
5724 ot->exec = uv_select_overlap_exec;
5725 ot->poll = ED_operator_uvedit;
5726
5727 /* properties */
5728 RNA_def_boolean(ot->srna,
5729 "extend",
5730 false,
5731 "Extend",
5732 "Extend selection rather than clearing the existing selection");
5733}
5734
5738
5739static float get_uv_vert_needle(const eUVSelectSimilar type,
5740 BMVert *vert,
5741 const float ob_m3[3][3],
5742 BMLoop *loop,
5743 const BMUVOffsets &offsets)
5744{
5745 BLI_assert(offsets.pin >= 0);
5746 BLI_assert(offsets.uv >= 0);
5747
5748 float result = 0.0f;
5749 switch (type) {
5750 case UV_SSIM_AREA_UV: {
5751 BMFace *f;
5752 BMIter iter;
5753 BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) {
5754 result += BM_face_calc_area_uv(f, offsets.uv);
5755 }
5756 break;
5757 }
5758 case UV_SSIM_AREA_3D: {
5759 BMFace *f;
5760 BMIter iter;
5761 BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) {
5763 }
5764 break;
5765 }
5766 case UV_SSIM_SIDES: {
5767 BMEdge *e;
5768 BMIter iter;
5769 BM_ITER_ELEM (e, &iter, vert, BM_EDGES_OF_VERT) {
5770 result += 1.0f;
5771 }
5772 break;
5773 }
5774 case UV_SSIM_PIN:
5775 return BM_ELEM_CD_GET_BOOL(loop, offsets.pin) ? 1.0f : 0.0f;
5776 default:
5778 return false;
5779 }
5780
5781 return result;
5782}
5783
5784static float get_uv_edge_needle(const eUVSelectSimilar type,
5785 BMEdge *edge,
5786 const float ob_m3[3][3],
5787 BMLoop *loop_a,
5788 BMLoop *loop_b,
5789 const BMUVOffsets &offsets)
5790{
5791 BLI_assert(offsets.pin >= 0);
5792 BLI_assert(offsets.uv >= 0);
5793 float result = 0.0f;
5794 switch (type) {
5795 case UV_SSIM_AREA_UV: {
5796 BMFace *f;
5797 BMIter iter;
5798 BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) {
5799 result += BM_face_calc_area_uv(f, offsets.uv);
5800 }
5801 break;
5802 }
5803 case UV_SSIM_AREA_3D: {
5804 BMFace *f;
5805 BMIter iter;
5806 BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) {
5808 }
5809 break;
5810 }
5811 case UV_SSIM_LENGTH_UV: {
5812 float *luv_a = BM_ELEM_CD_GET_FLOAT_P(loop_a, offsets.uv);
5813 float *luv_b = BM_ELEM_CD_GET_FLOAT_P(loop_b, offsets.uv);
5814 return len_v2v2(luv_a, luv_b);
5815 }
5816 case UV_SSIM_LENGTH_3D:
5817 return len_v3v3(edge->v1->co, edge->v2->co);
5818 case UV_SSIM_SIDES: {
5819 BMEdge *e;
5820 BMIter iter;
5821 BM_ITER_ELEM (e, &iter, edge, BM_FACES_OF_EDGE) {
5822 result += 1.0f;
5823 }
5824 break;
5825 }
5826 case UV_SSIM_PIN: {
5827 if (BM_ELEM_CD_GET_BOOL(loop_a, offsets.pin)) {
5828 result += 1.0f;
5829 }
5830 if (BM_ELEM_CD_GET_BOOL(loop_b, offsets.pin)) {
5831 result += 1.0f;
5832 }
5833 break;
5834 }
5835 default:
5837 return false;
5838 }
5839
5840 return result;
5841}
5842
5843static float get_uv_face_needle(const eUVSelectSimilar type,
5844 BMFace *face,
5845 int ob_index,
5846 const float ob_m3[3][3],
5847 const BMUVOffsets &offsets)
5848{
5849 BLI_assert(offsets.pin >= 0);
5850 BLI_assert(offsets.uv >= 0);
5851 float result = 0.0f;
5852 switch (type) {
5853 case UV_SSIM_AREA_UV:
5854 return BM_face_calc_area_uv(face, offsets.uv);
5855 case UV_SSIM_AREA_3D:
5856 return BM_face_calc_area_with_mat3(face, ob_m3);
5857 case UV_SSIM_SIDES:
5858 return face->len;
5859 case UV_SSIM_OBJECT:
5860 return ob_index;
5861 case UV_SSIM_PIN: {
5862 BMLoop *l;
5863 BMIter liter;
5864 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
5865 if (BM_ELEM_CD_GET_BOOL(l, offsets.pin)) {
5866 result += 1.0f;
5867 }
5868 }
5869 break;
5870 }
5871 case UV_SSIM_MATERIAL:
5872 return face->mat_nr;
5873 case UV_SSIM_WINDING:
5874 return signum_i(BM_face_calc_area_uv_signed(face, offsets.uv));
5875 default:
5877 return false;
5878 }
5879 return result;
5880}
5881
5883 const FaceIsland *island,
5884 const float ob_m3[3][3],
5885 const BMUVOffsets &offsets)
5886
5887{
5888 BLI_assert(offsets.uv >= 0);
5889 float result = 0.0f;
5890 switch (type) {
5891 case UV_SSIM_AREA_UV:
5892 for (int i = 0; i < island->faces_len; i++) {
5893 result += BM_face_calc_area_uv(island->faces[i], offsets.uv);
5894 }
5895 break;
5896 case UV_SSIM_AREA_3D:
5897 for (int i = 0; i < island->faces_len; i++) {
5898 result += BM_face_calc_area_with_mat3(island->faces[i], ob_m3);
5899 }
5900 break;
5901 case UV_SSIM_FACE:
5902 return island->faces_len;
5903 default:
5905 return false;
5906 }
5907 return result;
5908}
5909
5911{
5912 Scene *scene = CTX_data_scene(C);
5913 ViewLayer *view_layer = CTX_data_view_layer(C);
5916
5917 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
5918 const float threshold = RNA_float_get(op->ptr, "threshold");
5919 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
5920
5922 scene, view_layer, nullptr);
5923
5924 int max_verts_selected_all = 0;
5925 for (Object *ob : objects) {
5927 BMFace *face;
5928 BMIter iter;
5929 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
5930 if (!uvedit_face_visible_test(scene, face)) {
5931 continue;
5932 }
5933 max_verts_selected_all += face->len;
5934 }
5935 /* TODO: Get a tighter bounds */
5936 }
5937
5938 int tree_index = 0;
5939 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_verts_selected_all);
5940
5941 for (Object *ob : objects) {
5943 if (bm->totvertsel == 0) {
5944 continue;
5945 }
5946
5947 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5948 float ob_m3[3][3];
5949 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
5950
5951 BMFace *face;
5952 BMIter iter;
5953 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
5954 if (!uvedit_face_visible_test(scene, face)) {
5955 continue;
5956 }
5957 BMLoop *l;
5958 BMIter liter;
5959 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
5960 if (!uvedit_uv_select_test(scene, bm, l, offsets)) {
5961 continue;
5962 }
5963 float needle = get_uv_vert_needle(type, l->v, ob_m3, l, offsets);
5964 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
5965 }
5966 }
5967 }
5968
5969 if (tree_1d != nullptr) {
5970 BLI_kdtree_1d_deduplicate(tree_1d);
5971 BLI_kdtree_1d_balance(tree_1d);
5972 }
5973
5974 for (Object *ob : objects) {
5976 if (bm->totvertsel == 0) {
5977 /* No selection means no visible UV's unless sync-select is enabled. */
5978 if (!(ts->uv_flag & UV_FLAG_SELECT_SYNC)) {
5979 continue;
5980 }
5981 }
5982
5983 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
5985 }
5986
5987 bool changed = false;
5988
5989 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5990 float ob_m3[3][3];
5991 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
5992
5993 BMFace *face;
5994 BMIter iter;
5995 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
5996 if (!uvedit_face_visible_test(scene, face)) {
5997 continue;
5998 }
5999 BMLoop *l;
6000 BMIter liter;
6001 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
6002 if (uvedit_uv_select_test(scene, bm, l, offsets)) {
6003 continue; /* Already selected. */
6004 }
6005 const float needle = get_uv_vert_needle(type, l->v, ob_m3, l, offsets);
6006 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
6007 if (select) {
6008 uvedit_uv_select_set(scene, bm, l, select);
6009 changed = true;
6010 }
6011 }
6012 }
6013 if (changed) {
6014 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6015 if (bm->uv_select_sync_valid) {
6018 }
6019 else {
6021 }
6022 }
6023 else {
6024 uvedit_select_flush_from_verts(scene, bm, true);
6025 }
6027 }
6028 }
6029
6030 BLI_kdtree_1d_free(tree_1d);
6031 return OPERATOR_FINISHED;
6032}
6033
6035{
6036 Scene *scene = CTX_data_scene(C);
6037 ViewLayer *view_layer = CTX_data_view_layer(C);
6040
6041 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
6042 const float threshold = RNA_float_get(op->ptr, "threshold");
6043 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
6044
6046 scene, view_layer, nullptr);
6047
6048 int max_edges_selected_all = 0;
6049 for (Object *ob : objects) {
6051 BMFace *face;
6052 BMIter iter;
6053 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
6054 if (!uvedit_face_visible_test(scene, face)) {
6055 continue;
6056 }
6057 max_edges_selected_all += face->len;
6058 }
6059 /* TODO: Get a tighter bounds. */
6060 }
6061
6062 int tree_index = 0;
6063 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_edges_selected_all);
6064
6065 for (Object *ob : objects) {
6067 if (bm->totvertsel == 0) {
6068 continue;
6069 }
6070
6071 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
6072 float ob_m3[3][3];
6073 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
6074
6075 BMFace *face;
6076 BMIter iter;
6077 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
6078 if (!uvedit_face_visible_test(scene, face)) {
6079 continue;
6080 }
6081 BMLoop *l;
6082 BMIter liter;
6083 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
6084 if (!uvedit_edge_select_test(scene, bm, l, offsets)) {
6085 continue;
6086 }
6087
6088 float needle = get_uv_edge_needle(type, l->e, ob_m3, l, l->next, offsets);
6089 if (tree_1d) {
6090 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
6091 }
6092 }
6093 }
6094 }
6095
6096 if (tree_1d != nullptr) {
6097 BLI_kdtree_1d_deduplicate(tree_1d);
6098 BLI_kdtree_1d_balance(tree_1d);
6099 }
6100
6101 for (Object *ob : objects) {
6103 if (bm->totvertsel == 0) {
6104 /* No selection means no visible UV's unless sync-select is enabled. */
6105 if (!(ts->uv_flag & UV_FLAG_SELECT_SYNC)) {
6106 continue;
6107 }
6108 }
6109
6110 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6112 }
6113
6114 bool changed = false;
6115 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
6116 float ob_m3[3][3];
6117 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
6118
6119 BMFace *face;
6120 BMIter iter;
6121 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
6122 if (!uvedit_face_visible_test(scene, face)) {
6123 continue;
6124 }
6125 BMLoop *l;
6126 BMIter liter;
6127 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
6128 if (uvedit_edge_select_test(scene, bm, l, offsets)) {
6129 continue; /* Already selected. */
6130 }
6131
6132 float needle = get_uv_edge_needle(type, l->e, ob_m3, l, l->next, offsets);
6133 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
6134 if (select) {
6136 changed = true;
6137 }
6138 }
6139 }
6140 if (changed) {
6141 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6142 if (bm->uv_select_sync_valid) {
6145 }
6146 else {
6148 }
6149 }
6150 else {
6151 uvedit_select_flush_from_verts(scene, bm, true);
6152 }
6154 }
6155 }
6156
6157 BLI_kdtree_1d_free(tree_1d);
6158 return OPERATOR_FINISHED;
6159}
6160
6162{
6163 Scene *scene = CTX_data_scene(C);
6164 ViewLayer *view_layer = CTX_data_view_layer(C);
6167
6168 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
6169 const float threshold = RNA_float_get(op->ptr, "threshold");
6170 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
6171
6173 scene, view_layer, nullptr);
6174
6175 int max_faces_selected_all = 0;
6176 for (Object *ob : objects) {
6178 max_faces_selected_all += bm->totfacesel;
6179 /* TODO: Get a tighter bounds */
6180 }
6181
6182 int tree_index = 0;
6183 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_faces_selected_all);
6184
6185 for (const int ob_index : objects.index_range()) {
6186 Object *ob = objects[ob_index];
6188 if (bm->totvertsel == 0) {
6189 continue;
6190 }
6191
6192 float ob_m3[3][3];
6193 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
6194
6195 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
6196
6197 BMFace *face;
6198 BMIter iter;
6199 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
6200 if (!uvedit_face_visible_test(scene, face)) {
6201 continue;
6202 }
6203 if (!uvedit_face_select_test(scene, bm, face)) {
6204 continue;
6205 }
6206
6207 float needle = get_uv_face_needle(type, face, ob_index, ob_m3, offsets);
6208 if (tree_1d) {
6209 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
6210 }
6211 }
6212 }
6213
6214 if (tree_1d != nullptr) {
6215 BLI_kdtree_1d_deduplicate(tree_1d);
6216 BLI_kdtree_1d_balance(tree_1d);
6217 }
6218
6219 for (const int ob_index : objects.index_range()) {
6220 Object *ob = objects[ob_index];
6222 if (bm->totvertsel == 0) {
6223 /* No selection means no visible UV's unless sync-select is enabled. */
6224 if (!(ts->uv_flag & UV_FLAG_SELECT_SYNC)) {
6225 continue;
6226 }
6227 }
6228
6229 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6231 }
6232
6233 bool changed = false;
6234 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
6235
6236 float ob_m3[3][3];
6237 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
6238
6239 BMFace *face;
6240 BMIter iter;
6241 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
6242 if (!uvedit_face_visible_test(scene, face)) {
6243 continue;
6244 }
6245 if (uvedit_face_select_test(scene, bm, face)) {
6246 continue;
6247 }
6248
6249 float needle = get_uv_face_needle(type, face, ob_index, ob_m3, offsets);
6250
6251 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
6252 if (select) {
6253 uvedit_face_select_set(scene, bm, face, select);
6254 changed = true;
6255 }
6256 }
6257 if (changed) {
6258 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6259 if (bm->uv_select_sync_valid) {
6262 }
6263 else {
6265 }
6266 }
6267 else {
6268 uvedit_select_flush_from_verts(scene, bm, true);
6269 }
6271 }
6272 }
6273
6274 BLI_kdtree_1d_free(tree_1d);
6275 return OPERATOR_FINISHED;
6276}
6277
6278static bool uv_island_selected(const Scene *scene, const BMesh *bm, FaceIsland *island)
6279{
6280 BLI_assert(island && island->faces_len);
6281 return uvedit_face_select_test(scene, bm, island->faces[0]);
6282}
6283
6285{
6286 Scene *scene = CTX_data_scene(C);
6287 ViewLayer *view_layer = CTX_data_view_layer(C);
6290
6291 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
6292 const float threshold = RNA_float_get(op->ptr, "threshold");
6293 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
6294
6296 scene, view_layer, nullptr);
6297
6298 ListBase *island_list_ptr = MEM_calloc_arrayN<ListBase>(objects.size(), __func__);
6299 int island_list_len = 0;
6300
6301 const bool face_selected = !(scene->toolsettings->uv_flag & UV_FLAG_SELECT_SYNC);
6302
6303 for (const int ob_index : objects.index_range()) {
6304 Object *obedit = objects[ob_index];
6305 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
6306 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
6307 float aspect_y = 1.0f; /* Placeholder value, aspect doesn't change connectivity. */
6308 island_list_len += bm_mesh_calc_uv_islands(
6309 scene, bm, &island_list_ptr[ob_index], face_selected, false, false, aspect_y, offsets);
6310 }
6311
6312 FaceIsland **island_array = static_cast<FaceIsland **>(
6313 MEM_callocN(sizeof(*island_array) * island_list_len, __func__));
6314
6315 int tree_index = 0;
6316 KDTree_1d *tree_1d = BLI_kdtree_1d_new(island_list_len);
6317
6318 for (const int ob_index : objects.index_range()) {
6319 Object *obedit = objects[ob_index];
6320 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
6321
6322 float ob_m3[3][3];
6323 copy_m3_m4(ob_m3, obedit->object_to_world().ptr());
6324
6325 int index;
6326 LISTBASE_FOREACH_INDEX (FaceIsland *, island, &island_list_ptr[ob_index], index) {
6327 island_array[index] = island;
6328 if (!uv_island_selected(scene, bm, island)) {
6329 continue;
6330 }
6331 float needle = get_uv_island_needle(type, island, ob_m3, island->offsets);
6332 if (tree_1d) {
6333 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
6334 }
6335 }
6336 }
6337
6338 if (tree_1d != nullptr) {
6339 BLI_kdtree_1d_deduplicate(tree_1d);
6340 BLI_kdtree_1d_balance(tree_1d);
6341 }
6342
6343 int tot_island_index = 0;
6344 for (const int ob_index : objects.index_range()) {
6345 Object *obedit = objects[ob_index];
6346 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
6347
6348 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6350 }
6351
6352 float ob_m3[3][3];
6353 copy_m3_m4(ob_m3, obedit->object_to_world().ptr());
6354
6355 bool changed = false;
6356 int index;
6357 LISTBASE_FOREACH_INDEX (FaceIsland *, island, &island_list_ptr[ob_index], index) {
6358 island_array[tot_island_index++] = island; /* To deallocate later. */
6359 if (uv_island_selected(scene, bm, island)) {
6360 continue;
6361 }
6362 float needle = get_uv_island_needle(type, island, ob_m3, island->offsets);
6363 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
6364 if (!select) {
6365 continue;
6366 }
6367 for (int j = 0; j < island->faces_len; j++) {
6368 uvedit_face_select_set(scene, bm, island->faces[j], select);
6369 }
6370 changed = true;
6371 }
6372
6373 if (changed) {
6374 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6375 if (bm->uv_select_sync_valid) {
6378 }
6379 else {
6381 }
6382 }
6383 else {
6384 uvedit_select_flush_from_verts(scene, bm, true);
6385 }
6387 }
6388 }
6389
6390 BLI_assert(tot_island_index == island_list_len);
6391 for (int i = 0; i < island_list_len; i++) {
6392 MEM_SAFE_FREE(island_array[i]->faces);
6393 MEM_SAFE_FREE(island_array[i]);
6394 }
6395
6396 MEM_SAFE_FREE(island_array);
6397 MEM_SAFE_FREE(island_list_ptr);
6398 BLI_kdtree_1d_free(tree_1d);
6399
6400 return OPERATOR_FINISHED;
6401}
6402
6403/* Select similar UV faces/edges/verts based on current selection. */
6405{
6407 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
6408 const bool use_select_linked = ED_uvedit_select_island_check(ts);
6409
6410 if (!RNA_property_is_set(op->ptr, prop)) {
6412 }
6413 else {
6414 ts->select_thresh = RNA_property_float_get(op->ptr, prop);
6415 }
6416
6417 const int selectmode = (ts->uv_flag & UV_FLAG_SELECT_SYNC) ? ts->selectmode : ts->uv_selectmode;
6418 if (use_select_linked) {
6419 return uv_select_similar_island_exec(C, op);
6420 }
6421 if (selectmode & UV_SELECT_FACE) {
6422 return uv_select_similar_face_exec(C, op);
6423 }
6424 if (selectmode & UV_SELECT_EDGE) {
6425 return uv_select_similar_edge_exec(C, op);
6426 }
6427 /* #UV_SELECT_VERT */
6428 return uv_select_similar_vert_exec(C, op);
6429}
6430
6432 {UV_SSIM_PIN, "PIN", 0, "Pinned", ""},
6433 {UV_SSIM_LENGTH_UV, "LENGTH", 0, "Length", "Edge length in UV space"},
6434 {UV_SSIM_LENGTH_3D, "LENGTH_3D", 0, "Length 3D", "Length of edge in 3D space"},
6435 {UV_SSIM_AREA_UV, "AREA", 0, "Area", "Face area in UV space"},
6436 {UV_SSIM_AREA_3D, "AREA_3D", 0, "Area 3D", "Area of face in 3D space"},
6437 {UV_SSIM_MATERIAL, "MATERIAL", 0, "Material", ""},
6438 {UV_SSIM_OBJECT, "OBJECT", 0, "Object", ""},
6439 {UV_SSIM_SIDES, "SIDES", 0, "Polygon Sides", ""},
6441 "WINDING",
6442 0,
6443 "Winding",
6444 "Face direction defined by (clockwise or anti-clockwise winding (facing up or facing down)"},
6445 {UV_SSIM_FACE, "FACE", 0, "Amount of Faces in Island", ""},
6446 {0}};
6447
6448static EnumPropertyItem prop_similar_compare_types[] = {{SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
6449 {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
6450 {SIM_CMP_LT, "LESS", 0, "Less", ""},
6451 {0}};
6452
6454 PointerRNA * /*ptr*/,
6455 PropertyRNA * /*prop*/,
6456 bool *r_free)
6457{
6458 EnumPropertyItem *item = nullptr;
6459 int totitem = 0;
6460
6462 if (ts) {
6463 const bool use_select_linked = ED_uvedit_select_island_check(ts);
6464 const int selectmode = (ts->uv_flag & UV_FLAG_SELECT_SYNC) ? ts->selectmode :
6465 ts->uv_selectmode;
6466 /* TODO: co-exist with selection modes. */
6467 if (use_select_linked) {
6471 }
6472 else if (selectmode & UV_SELECT_FACE) {
6479 }
6480 else if (selectmode & UV_SELECT_EDGE) {
6484 }
6485 else {
6486 /* #UV_SELECT_VERT */
6488 }
6489 }
6490 else {
6492 }
6493
6494 RNA_enum_item_end(&item, &totitem);
6495 *r_free = true;
6496 return item;
6497}
6498
6500{
6501 /* identifiers */
6502 ot->name = "Select Similar";
6503 ot->description = "Select similar UVs by property types";
6504 ot->idname = "UV_OT_select_similar";
6505
6506 /* API callbacks. */
6507 ot->invoke = WM_menu_invoke;
6508 ot->exec = uv_select_similar_exec;
6510
6511 /* flags */
6512 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6513
6514 /* properties */
6515 PropertyRNA *prop = ot->prop = RNA_def_enum(
6516 ot->srna, "type", uv_select_similar_type_items, SIMVERT_NORMAL, "Type", "");
6519 RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
6520 RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
6521}
6522
6524
6525/* -------------------------------------------------------------------- */
6531
6532BMFace **ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
6533{
6534 CLAMP_MAX(len_max, bm->totface);
6535 int faces_len = 0;
6536 BMFace **faces = MEM_malloc_arrayN<BMFace *>(len_max, __func__);
6537
6538 BMIter iter;
6539 BMFace *f;
6540 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
6541 if (uvedit_face_visible_test(scene, f)) {
6542 if (uvedit_face_select_test(scene, bm, f)) {
6543 faces[faces_len++] = f;
6544 if (faces_len == len_max) {
6545 goto finally;
6546 }
6547 }
6548 }
6549 }
6550
6551finally:
6552 *r_faces_len = faces_len;
6553 if (faces_len != len_max) {
6554 faces = static_cast<BMFace **>(MEM_reallocN(faces, sizeof(*faces) * faces_len));
6555 }
6556 return faces;
6557}
6558
6559BMLoop **ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
6560{
6561 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
6562 BLI_assert(offsets.uv >= 0);
6563
6564 CLAMP_MAX(len_max, bm->totloop);
6565 int edges_len = 0;
6566 BMLoop **edges = MEM_malloc_arrayN<BMLoop *>(len_max, __func__);
6567
6568 BMIter iter;
6569 BMFace *f;
6570
6571 /* Clear tag. */
6572 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
6573 BMIter liter;
6574 BMLoop *l_iter;
6575 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
6577 }
6578 }
6579
6580 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
6581 if (uvedit_face_visible_test(scene, f)) {
6582 BMIter liter;
6583 BMLoop *l_iter;
6584 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
6585 if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
6586 if (uvedit_edge_select_test(scene, bm, l_iter, offsets)) {
6588
6589 edges[edges_len++] = l_iter;
6590 if (edges_len == len_max) {
6591 goto finally;
6592 }
6593
6594 /* Tag other connected loops so we don't consider them separate edges. */
6595 if (l_iter != l_iter->radial_next) {
6596 BMLoop *l_radial_iter = l_iter->radial_next;
6597 do {
6598 if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, offsets.uv)) {
6599 BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG);
6600 }
6601 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
6602 }
6603 }
6604 }
6605 }
6606 }
6607 }
6608
6609finally:
6610 *r_edges_len = edges_len;
6611 if (edges_len != len_max) {
6612 edges = static_cast<BMLoop **>(MEM_reallocN(edges, sizeof(*edges) * edges_len));
6613 }
6614 return edges;
6615}
6616
6617BMLoop **ED_uvedit_selected_verts(const Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
6618{
6619 const ToolSettings *ts = scene->toolsettings;
6620 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
6621 BLI_assert(offsets.uv >= 0);
6622
6623 CLAMP_MAX(len_max, bm->totloop);
6624 int verts_len = 0;
6625 BMLoop **verts = MEM_malloc_arrayN<BMLoop *>(len_max, __func__);
6626
6627 BMIter iter;
6628 BMFace *f;
6629
6630 /* Clear tag. */
6631 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
6632 BMIter liter;
6633 BMLoop *l_iter;
6634 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
6636 }
6637 }
6638
6639 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
6640 if (uvedit_face_visible_test(scene, f)) {
6641 BMIter liter;
6642 BMLoop *l_iter;
6643 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
6644 if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
6645 if (uvedit_vert_select_get_no_sync(ts, bm, l_iter)) {
6647
6648 verts[verts_len++] = l_iter;
6649 if (verts_len == len_max) {
6650 goto finally;
6651 }
6652
6653 /* Tag other connected loops so we don't consider them separate vertices. */
6654 BMIter liter_disk;
6655 BMLoop *l_disk_iter;
6656 BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) {
6657 if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, offsets.uv)) {
6658 BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG);
6659 }
6660 }
6661 }
6662 }
6663 }
6664 }
6665 }
6666
6667finally:
6668 *r_verts_len = verts_len;
6669 if (verts_len != len_max) {
6670 verts = static_cast<BMLoop **>(MEM_reallocN(verts, sizeof(*verts) * verts_len));
6671 }
6672 return verts;
6673}
6674
6676
6677/* -------------------------------------------------------------------- */
6680
6681void ED_uvedit_selectmode_clean(const Scene *scene, Object *obedit)
6682{
6683 const ToolSettings *ts = scene->toolsettings;
6685 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
6686 char sticky = ts->uv_sticky;
6687
6689 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
6690 BMFace *efa;
6691 BMLoop *l;
6692 BMIter iter, liter;
6693
6694 if (ts->uv_selectmode == UV_SELECT_VERT) {
6695 /* Vertex mode. */
6696 if (sticky != UV_STICKY_DISABLE) {
6698 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
6699 if (!uvedit_face_visible_test(scene, efa)) {
6700 continue;
6701 }
6702 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
6703 if (uvedit_uv_select_test(scene, bm, l, offsets)) {
6705 }
6706 }
6707 }
6708 uv_select_flush_from_tag_loop(scene, obedit, true);
6709 }
6710 }
6711
6712 else if (ts->uv_selectmode == UV_SELECT_EDGE) {
6713 /* Edge mode. */
6714 if (sticky != UV_STICKY_DISABLE) {
6715 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
6716 if (!uvedit_face_visible_test(scene, efa)) {
6717 continue;
6718 }
6719 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
6720 if (uvedit_edge_select_test(scene, bm, l, offsets)) {
6721 uvedit_edge_select_set_noflush(scene, bm, l, true, sticky, offsets);
6722 }
6723 }
6724 }
6725 }
6727 }
6728
6729 else if (ts->uv_selectmode == UV_SELECT_FACE) {
6730 /* Face mode. */
6731 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
6733 if (uvedit_face_visible_test(scene, efa)) {
6734 if (uvedit_face_select_test(scene, bm, efa)) {
6736 }
6737 uvedit_face_select_set(scene, bm, efa, false);
6738 }
6739 }
6740 uv_select_flush_from_tag_face(scene, obedit, true);
6741 }
6742
6744}
6746{
6747 Scene *scene = CTX_data_scene(C);
6748 ToolSettings *ts = scene->toolsettings;
6749 ViewLayer *view_layer = CTX_data_view_layer(C);
6751
6753 scene, view_layer, nullptr);
6754 for (Object *obedit : objects) {
6755 ED_uvedit_selectmode_clean(scene, obedit);
6756
6758 }
6759}
6760
6762{
6763 Scene *scene = CTX_data_scene(C);
6764 ToolSettings *ts = scene->toolsettings;
6765 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0) {
6766 return;
6767 }
6768 /* Only for edge/face select modes. */
6769 if (ts->selectmode & SCE_SELECT_VERTEX) {
6770 return;
6771 }
6772
6773 ViewLayer *view_layer = CTX_data_view_layer(C);
6776 scene, view_layer, nullptr);
6777 for (Object *obedit : objects) {
6779 }
6780}
6781
6786{
6787 const Scene *scene = CTX_data_scene(C);
6788 const ToolSettings *ts = scene->toolsettings;
6789 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6790 return;
6791 }
6792
6793 ViewLayer *view_layer = CTX_data_view_layer(C);
6795 scene, view_layer, nullptr);
6796 for (Object *obedit : objects) {
6797 uv_select_sync_update(scene, obedit);
6798 }
6799}
6800
6802{
6803 Scene *scene = CTX_data_scene(C);
6804 ToolSettings *ts = scene->toolsettings;
6805 const char new_uv_selectmode = RNA_enum_get(op->ptr, "type");
6806
6807 /* Early exit if no change in current selection mode */
6808 if (new_uv_selectmode == ts->uv_selectmode) {
6809 return OPERATOR_CANCELLED;
6810 }
6811
6812 /* Set new UV select mode. */
6813 ts->uv_selectmode = new_uv_selectmode;
6814
6815 /* Handle UV selection states according to new select mode and sticky mode. */
6817
6820
6821 return OPERATOR_FINISHED;
6822}
6823
6825 wmOperator *op,
6826 const wmEvent * /*event*/)
6827{
6829 const SpaceImage *sima = CTX_wm_space_image(C);
6830
6831 /* Could be removed? - Already done in poll callback. */
6832 if ((!sima) || (sima->mode != SI_MODE_UV)) {
6833 return OPERATOR_CANCELLED;
6834 }
6835 /* Pass through when UV sync selection is enabled.
6836 * Allow for mesh select-mode key-map. */
6837 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
6838 return OPERATOR_PASS_THROUGH;
6839 }
6840
6841 return uv_select_mode_exec(C, op);
6842}
6843
6845{
6846 /* identifiers */
6847 ot->name = "UV Select Mode";
6848 ot->description = "Change UV selection mode";
6849 ot->idname = "UV_OT_select_mode";
6850
6851 /* API callbacks. */
6852 ot->invoke = uv_select_mode_invoke;
6853 ot->exec = uv_select_mode_exec;
6855
6856 /* flags */
6857 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6858
6859 /* RNA props */
6860 PropertyRNA *prop;
6861 ot->prop = prop = RNA_def_enum(
6862 ot->srna, "type", rna_enum_mesh_select_mode_uv_items, 0, "Type", "");
6864}
6865
6878
6880{
6881 /* identifiers */
6882 ot->name = "Set User Region";
6883 ot->description = "Set the boundaries of the user region";
6884 ot->idname = "UV_OT_custom_region_set";
6885
6886 /* API callbacks. */
6887 ot->invoke = WM_gesture_box_invoke;
6889 ot->modal = WM_gesture_box_modal;
6891 ot->cancel = WM_gesture_box_cancel;
6892
6893 /* flags */
6894 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6895
6896 /* properties */
6898}
6899
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)
int CustomData_get_active_layer(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(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:61
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
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
struct GSet GSet
Definition BLI_ghash.h:337
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.cc:936
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
Definition BLI_hash.h:51
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:191
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:171
void BLI_heap_clear(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:213
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:365
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:291
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
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
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 UNUSED_FUNCTION(x)
#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)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
@ UV_SELECT_VERT
@ UV_SELECT_FACE
@ UV_SELECT_EDGE
@ UV_STICKY_VERT
@ UV_STICKY_LOCATION
@ UV_STICKY_DISABLE
@ UV_FLAG_SELECT_ISLAND
@ UV_FLAG_CUSTOM_REGION
@ UV_FLAG_SELECT_SYNC
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ SI_MODE_UV
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ 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)
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
bool EDBM_uvselect_clear(BMEditMesh *em)
void EDBM_select_more(BMEditMesh *em, bool use_face_step)
void EDBM_select_swap(BMEditMesh *em)
UvVertMap * BM_uv_vert_map_create(BMesh *bm, bool use_select, bool respect_hide)
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 BM_uv_vert_map_free(UvVertMap *vmap)
bool ED_operator_uvedit_space_image(bContext *C)
bool ED_operator_uvedit(bContext *C)
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
eSelectOp ED_select_op_modal(eSelectOp sel_op, bool is_first)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
#define SEL_OP_USE_PRE_DESELECT(sel_op)
std::string ED_select_circle_get_name(wmOperatorType *ot, PointerRNA *ptr)
SelectPick_Params ED_select_pick_params_from_operator(PointerRNA *ptr) 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)
std::string ED_select_pick_get_name(wmOperatorType *ot, PointerRNA *ptr)
void uvedit_edge_select_set_noflush(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const int sticky_flag, const BMUVOffsets &offsets)
void ED_uvedit_selectmode_clean(const Scene *scene, Object *obedit)
UV Select Mode set.
bool uvedit_edge_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void ED_uvedit_selectmode_clean_multi(bContext *C)
bool uvedit_loop_vert_select_get(const ToolSettings *ts, const BMesh *bm, const BMLoop *l)
void ED_uvedit_sticky_selectmode_update(bContext *C)
void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
bool ED_uvedit_sync_uvselect_ignore(const ToolSettings *ts)
void uvedit_select_flush_from_verts(const Scene *scene, BMesh *bm, bool select)
void uvedit_face_select_set_with_sticky(const Scene *scene, BMesh *bm, BMFace *efa, bool select, const BMUVOffsets &offsets)
BMLoop ** ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
char ED_uvedit_select_mode_get(const Scene *scene)
void ED_uvedit_selectmode_flush(const Scene *scene, BMesh *bm)
UV Select Mode Flush.
bool uvedit_edge_select_test_ex(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
bool uvedit_uv_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void uvedit_edge_select_set_with_sticky(const Scene *scene, BMesh *bm, BMLoop *l, bool select, const BMUVOffsets &offsets)
void uvedit_loop_vert_select_set(const ToolSettings *ts, const BMesh *bm, BMLoop *l, const bool select)
void uvedit_uv_select_set_with_sticky(const Scene *scene, BMesh *bm, BMLoop *l, bool select, const BMUVOffsets &offsets)
bool uvedit_face_select_test_ex(const ToolSettings *ts, const BMesh *bm, const BMFace *efa)
void uvedit_uv_select_set(const Scene *scene, BMesh *bm, BMLoop *l, bool select)
Select UV Vertex.
bool ED_uvedit_select_island_check(const ToolSettings *ts)
bool uvedit_loop_edge_select_get(const ToolSettings *ts, const BMesh *bm, const BMLoop *l)
void uvedit_uv_select_enable(const Scene *scene, BMesh *bm, BMLoop *l)
bool ED_uvedit_sync_uvselect_is_valid_or_ignore(const ToolSettings *ts, const BMesh *bm)
void ED_uvedit_select_sync_multi(bContext *C)
void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMesh *bm, bool select)
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, const BMUVOffsets &offsets)
void uvedit_loop_edge_select_set(const ToolSettings *ts, const BMesh *bm, BMLoop *l, const bool select)
bool uvedit_face_visible_test(const Scene *scene, const BMFace *efa)
void uvedit_edge_select_set(const Scene *scene, BMesh *bm, BMLoop *l, bool select)
Select UV Edge.
void ED_uvedit_deselect_all(const Scene *scene, Object *obedit, int action)
void uvedit_face_select_set(const Scene *scene, BMesh *bm, BMFace *efa, bool select)
Select UV Face.
BMFace ** ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
bool uvedit_face_select_test(const Scene *scene, const BMesh *bm, const BMFace *efa)
bool uvedit_face_visible_test_ex(const ToolSettings *ts, const BMFace *efa)
BMLoop ** ED_uvedit_selected_verts(const Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
void ED_uvedit_active_vert_loop_set(BMesh *bm, BMLoop *l)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
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:1751
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:1702
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:1739
float UI_view2d_scale_get_y(const View2D *v2d)
Definition view2d.cc:1925
void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1912
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:1668
#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:1675
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1921
#define NC_GEOM
Definition WM_types.hh:393
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_TOOLSETTINGS
Definition WM_types.hh:449
#define ND_SPACE_IMAGE
Definition WM_types.hh:522
#define ND_SELECT
Definition WM_types.hh:508
#define NC_SPACE
Definition WM_types.hh:392
#define U
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_ELEM_SELECT_UV_EDGE
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_SELECT_UV
@ BM_ELEM_TAG
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_test_bool(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
bool BM_uv_map_attr_pin_exists(const BMesh *bm, const StringRef uv_map_name)
#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)
BMesh * bm
void BM_select_history_clear(BMesh *bm)
void BM_mesh_select_mode_flush(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)
void BM_mesh_select_flush_from_verts(BMesh *bm, const bool select)
#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_offsets_get(const BMesh *bm)
bool BM_loop_uv_share_edge_check(const BMLoop *l_a, const BMLoop *l_b, const int cd_loop_uv_offset)
bool BM_loop_uv_share_vert_check(const BMLoop *l_a, const 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)
bool BM_loop_edge_uvselect_check_other_face(BMLoop *l, const char hflag, const int cd_loop_uv_offset)
void BM_mesh_uvselect_flush_from_faces_only_select(BMesh *bm)
void BM_mesh_uvselect_sync_from_mesh_sticky_vert(BMesh *bm)
void BM_mesh_uvselect_mode_flush(BMesh *bm)
void BM_face_uvselect_set_noflush(BMesh *bm, BMFace *f, bool select)
void BM_face_uvselect_set(BMesh *bm, BMFace *f, bool select)
void BM_mesh_uvselect_mode_flush_update(BMesh *bm, const short selectmode_old, const short selectmode_new, const int cd_loop_uv_offset)
void BM_mesh_uvselect_sync_from_mesh_sticky_disabled(BMesh *bm)
void BM_mesh_uvselect_sync_to_mesh(BMesh *bm)
void BM_mesh_uvselect_flush_shared_only_select(BMesh *bm, const int cd_loop_uv_offset)
bool BM_loop_edge_uvselect_test(const BMLoop *l)
void BM_loop_edge_uvselect_set_noflush(BMesh *bm, BMLoop *l, bool select)
void BM_mesh_uvselect_flush_from_faces_only_deselect(BMesh *bm)
void BM_mesh_uvselect_flush_from_loop_verts_only_select(BMesh *bm)
bool BM_mesh_uvselect_clear(BMesh *bm)
void BM_mesh_uvselect_flush_from_loop_verts_only_deselect(BMesh *bm)
void BM_loop_edge_uvselect_set(BMesh *bm, BMLoop *l, bool select)
void BM_mesh_uvselect_sync_from_mesh_sticky_location(BMesh *bm, const int cd_loop_uv_offset)
void BM_mesh_uvselect_flush_from_loop_edges(BMesh *bm, bool flush_down)
void BM_mesh_uvselect_set_elem_from_mesh(BMesh *bm, const bool select, const BMUVSelectPickParams &params, const blender::VectorList< BMVert * > &verts, const blender::VectorList< BMEdge * > &edges, const blender::VectorList< BMFace * > &faces)
void BM_loop_vert_uvselect_set_noflush(BMesh *bm, BMLoop *l, bool select)
void BM_loop_vert_uvselect_set_shared(BMesh *bm, BMLoop *l, bool select, const int cd_loop_uv_offset)
void BM_loop_edge_uvselect_set_shared(BMesh *bm, BMLoop *l, bool select, const int cd_loop_uv_offset)
bool BM_loop_vert_uvselect_check_other_face(BMLoop *l, const char hflag, const int cd_loop_uv_offset)
BPy_StructRNA * depsgraph
bool is_empty() const
Definition BLI_array.hh:264
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
IndexRange index_range() const
void vert_select_set(BMVert *v, bool value)
void face_select_set(BMFace *f, bool value)
void edge_select_set(BMEdge *f, bool value)
static std::unique_ptr< UVSyncSelectFromMesh > create_if_needed(const ToolSettings &ts, BMesh &bm)
nullptr float
static const EnumPropertyItem prop_similar_compare_types[]
static ushort indices[]
static float verts[][3]
#define shared
#define select(A, B, C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
VecBase< int32_t, 2 > int2
return ret
#define sqrtf
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:131
#define FLT_MAX
Definition stdcycles.h:14
BMVert * v1
BMVert * v2
struct BMLoop * l
short selectmode
struct BMEditSelection * prev
short mat_nr
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
bool uv_select_sync_valid
BMFace ** ftable
BMFace ** faces
Definition ED_uvedit.hh:367
Definition DNA_ID.h:414
struct ToolSettings * toolsettings
float tri[3][2]
unsigned int face_index
UvMapVert * next
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
bool uv_find_nearest_vert_multi(Scene *scene, blender::Span< Object * > objects, const float co[2], float penalty_dist, UvNearestHit *hit)
void UV_OT_select_all(wmOperatorType *ot)
void UV_OT_select_edge_ring(wmOperatorType *ot)
void uvedit_face_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMFace *f, bool select)
bool uvedit_vert_select_get_no_sync(const ToolSettings *ts, const BMesh *bm, const BMLoop *l)
void UV_OT_select(wmOperatorType *ot)
void uvedit_select_prepare_sync_select(const Scene *scene, BMesh *bm)
bool uv_find_nearest_face_multi(Scene *scene, blender::Span< Object * > objects, const float co[2], UvNearestHit *hit)
void UV_OT_select_split(wmOperatorType *ot)
void UV_OT_select_linked(wmOperatorType *ot)
bool uvedit_select_is_any_selected_multi(const Scene *scene, blender::Span< Object * > objects)
const float * uvedit_first_selected_uv_from_vertex(Scene *scene, const BMesh *bm, BMVert *eve, const BMUVOffsets &offsets)
bool uvedit_vert_is_face_select_any_other(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void UV_OT_select_circle(wmOperatorType *ot)
UvNearestHit uv_nearest_hit_init_max_default()
void UV_OT_select_mode(wmOperatorType *ot)
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d)
void UV_OT_select_similar(wmOperatorType *ot)
bool uvedit_edge_is_face_select_any_other(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void UV_OT_select_linked_pick(wmOperatorType *ot)
void UV_OT_custom_region_set(wmOperatorType *ot)
void UV_OT_select_more(wmOperatorType *ot)
void UV_OT_select_pinned(wmOperatorType *ot)
void UV_OT_select_loop(wmOperatorType *ot)
UvNearestHit uv_nearest_hit_init_dist_px(const View2D *v2d, float dist_px)
bool uvedit_vert_is_all_other_faces_selected(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void uvedit_edge_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMLoop *l, bool select)
void UV_OT_select_overlap(wmOperatorType *ot)
bool uvedit_edge_select_get_no_sync(const ToolSettings *ts, const BMesh *bm, const BMLoop *l)
void UV_OT_select_lasso(wmOperatorType *ot)
bool uvedit_select_is_any_selected(const Scene *scene, BMesh *bm)
void uvedit_select_prepare_custom_data(const Scene *scene, BMesh *bm)
bool uv_find_nearest_edge_multi(Scene *scene, blender::Span< Object * > objects, const float co[2], float penalty, UvNearestHit *hit)
void uvedit_vert_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMLoop *l, bool select)
void UV_OT_select_less(wmOperatorType *ot)
void UV_OT_select_box(wmOperatorType *ot)
bool uv_find_nearest_face_multi_ex(Scene *scene, blender::Span< Object * > objects, const float co[2], UvNearestHit *hit, bool only_in_face)
static float get_uv_island_needle(const eUVSelectSimilar type, const FaceIsland *island, const float ob_m3[3][3], const BMUVOffsets &offsets)
static bool uv_mouse_select(bContext *C, const float co[2], const SelectPick_Params &params)
void uvedit_edge_select_set_noflush(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const int sticky_flag, const BMUVOffsets &offsets)
void uvedit_face_select_set(const Scene *scene, BMesh *bm, BMFace *efa, const bool select)
Select UV Face.
static bool uv_circle_select_is_point_inside(const float uv[2], const float offset[2], const float ellipse[2])
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)
BMLoop * ED_uvedit_active_vert_loop_get(const ToolSettings *ts, BMesh *bm)
bool uvedit_vert_is_edge_select_any_other(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
bool uvedit_edge_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
#define SET_SELECTION(value)
static wmOperatorStatus uv_select_overlap_exec(bContext *C, wmOperator *op)
void uvedit_uv_select_set_with_sticky(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
bool uvedit_loop_vert_select_get(const ToolSettings *ts, const BMesh *bm, const BMLoop *l)
static void uv_select_tag_update_for_object(Depsgraph *depsgraph, const ToolSettings *ts, Object *obedit)
void uvedit_face_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMFace *f, bool select)
bool uvedit_vert_select_get_no_sync(const ToolSettings *ts, const BMesh *bm, const BMLoop *l)
static wmOperatorStatus uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick)
void uvedit_edge_select_set(const Scene *scene, BMesh *bm, BMLoop *l, const bool select)
Select UV Edge.
void uvedit_face_select_set_with_sticky(const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const BMUVOffsets &offsets)
void uvedit_uv_select_shared_vert(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const int sticky_flag, const BMUVOffsets &offsets)
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 ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
void uvedit_select_prepare_sync_select(const Scene *scene, BMesh *bm)
static wmOperatorStatus uv_select_more_less(bContext *C, const bool select)
static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset)
bool ED_uvedit_sync_uvselect_ignore(const ToolSettings *ts)
static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
static BMLoop * bm_select_edgeloop_single_side_next(const Scene *scene, BMLoop *l_step, BMVert *v_from, const BMUVOffsets &offsets)
static EnumPropertyItem uv_select_similar_type_items[]
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)
bool uvedit_vert_is_face_select_any_other(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
static bool bm_loop_select_edge_check_internal(const Scene *scene, BMesh *bm, BMLoop *l)
static void uvedit_edge_select_flush_from_tag_sticky_loc_internal(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v)
void uvedit_select_prepare_UNUSED(const Scene *scene, BMesh *bm)
static wmOperatorStatus uv_custom_region_set_exec(bContext *C, wmOperator *op)
static wmOperatorStatus uv_select_more_exec(bContext *C, wmOperator *)
void uvedit_uv_select_disable(const Scene *scene, BMesh *bm, BMLoop *l)
char ED_uvedit_select_mode_get(const Scene *scene)
static bool uv_island_selected(const Scene *scene, const BMesh *bm, FaceIsland *island)
bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
bool uvedit_edge_select_test_ex(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void ED_uvedit_sync_uvselect_ensure_if_needed(const ToolSettings *ts, BMesh *bm)
#define NEIGHBORING_FACE_IS_SEL
bool uvedit_uv_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
static wmOperatorStatus uv_mouse_select_loop_generic_multi(bContext *C, const Span< Object * > objects, const float co[2], const bool extend, enum eUVLoopGenericType loop_type)
static wmOperatorStatus uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static BMLoop * bm_select_edgeloop_double_side_next(const Scene *scene, BMLoop *l_step, BMVert *v_from, const BMUVOffsets &offsets)
UvNearestHit uv_nearest_hit_init_max_default()
void uvedit_edge_select_set_with_sticky(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
bool uv_find_nearest_vert(Scene *scene, Object *obedit, float const co[2], const float penalty_dist, UvNearestHit *hit)
void uvedit_loop_vert_select_set(const ToolSettings *ts, const BMesh *bm, BMLoop *l, const bool select)
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d)
static wmOperatorStatus uv_circle_select_exec(bContext *C, wmOperator *op)
bool uvedit_face_select_get_no_sync(const ToolSettings *ts, const BMesh *bm, const BMFace *f)
static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select)
static wmOperatorStatus uv_select_pinned_exec(bContext *C, wmOperator *op)
bool uvedit_edge_is_face_select_any_other(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
static wmOperatorStatus uv_select_similar_island_exec(bContext *C, wmOperator *op)
void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMesh *bm, const bool select)
void uvedit_edge_select_enable(const Scene *scene, BMesh *bm, BMLoop *l)
static wmOperatorStatus uv_select_exec(bContext *C, wmOperator *op)
void uvedit_edge_select_disable(const Scene *scene, BMesh *bm, BMLoop *l)
BMLoop * uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, const float co[2])
static bool uvedit_select_pin_ok_or_report(const Scene *scene, ReportList *reports)
void uvedit_face_select_enable(const Scene *scene, BMesh *bm, BMFace *efa)
static wmOperatorStatus uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool uvedit_face_select_test_ex(const ToolSettings *ts, const BMesh *bm, const BMFace *efa)
static wmOperatorStatus uv_select_mode_exec(bContext *C, wmOperator *op)
static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select)
bool uv_find_nearest_face_multi(Scene *scene, const Span< Object * > objects, const float co[2], UvNearestHit *hit)
bool ED_uvedit_select_island_check(const ToolSettings *ts)
static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
static wmOperatorStatus uv_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static const EnumPropertyItem * uv_select_similar_type_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static void uv_select_edgeloop_double_side_tag(const Scene *scene, BMesh *bm, BMLoop *l_init_pair[2], const BMUVOffsets &offsets)
static wmOperatorStatus uv_select_split_exec(bContext *C, wmOperator *op)
bool uvedit_loop_edge_select_get(const ToolSettings *ts, const BMesh *bm, const BMLoop *l)
static bool do_lasso_select_mesh_uv(bContext *C, const Span< int2 > mcoords, const eSelectOp sel_op)
static void uvedit_uv_select_flush_from_tag_sticky_loc_internal(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
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)
void uvedit_uv_select_enable(const Scene *scene, BMesh *bm, BMLoop *l)
bool ED_uvedit_sync_uvselect_is_valid_or_ignore(const ToolSettings *ts, const BMesh *bm)
static wmOperatorStatus uv_select_similar_edge_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])
static wmOperatorStatus uv_mouse_select_loop_generic(bContext *C, const float co[2], const bool extend, enum eUVLoopGenericType loop_type)
bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit)
static float get_uv_vert_needle(const eUVSelectSimilar type, BMVert *vert, const float ob_m3[3][3], BMLoop *loop, const BMUVOffsets &offsets)
static wmOperatorStatus uv_select_similar_face_exec(bContext *C, wmOperator *op)
static void uv_select_linked_multi_for_select_island(const Scene *scene, const Span< Object * > objects, Object *obedit, BMFace *efa, const bool deselect, const bool select_faces, const char hflag)
static wmOperatorStatus uv_box_select_exec(bContext *C, wmOperator *op)
bool uv_find_nearest_vert_multi(Scene *scene, const Span< Object * > objects, float const co[2], const float penalty_dist, UvNearestHit *hit)
static wmOperatorStatus uv_select_edge_ring_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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)
void uvedit_uv_select_set(const Scene *scene, BMesh *bm, BMLoop *l, const bool select)
Select UV Vertex.
#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])
bool uv_find_nearest_edge_multi(Scene *scene, const Span< Object * > objects, const float co[2], const float penalty, UvNearestHit *hit)
bool uvedit_vert_is_all_other_faces_selected(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void uvedit_loop_edge_select_set(const ToolSettings *ts, const BMesh *bm, BMLoop *l, const bool select)
static wmOperatorStatus uv_lasso_select_exec(bContext *C, wmOperator *op)
void uvedit_edge_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMLoop *l, bool select)
static wmOperatorStatus uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void uv_select_all(const Scene *scene, BMEditMesh *em, bool select_all)
static wmOperatorStatus uv_select_linked_pick_exec(bContext *C, wmOperator *op)
static void bm_clear_uv_vert_selection(const Scene *scene, BMesh *bm)
bool uvedit_face_visible_test(const Scene *scene, const BMFace *efa)
BMLoop * uv_find_nearest_loop_from_vert(Scene *scene, Object *obedit, BMVert *v, const float co[2])
bool uvedit_uv_select_test_ex(const ToolSettings *ts, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
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])
static wmOperatorStatus uv_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *)
void uvedit_edge_select_shared_vert(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const int sticky_flag, const BMUVOffsets &offsets)
bool uv_find_nearest_face_ex(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit, const bool only_in_face)
static BMLoop * uvedit_loop_find_other_radial_loop_with_visible_face(const Scene *scene, BMLoop *l_src, const BMUVOffsets &offsets)
bool uvedit_edge_select_get_no_sync(const ToolSettings *ts, const BMesh *bm, const BMLoop *l)
void uvedit_face_select_shared_vert(const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const BMUVOffsets &offsets)
static void uv_select_sync_update(const Scene *scene, Object *obedit)
BMLoop * ED_uvedit_active_edge_loop_get(const ToolSettings *ts, BMesh *bm)
static bool uv_mouse_select_multi(bContext *C, const Span< Object * > objects, const float co[2], const SelectPick_Params &params)
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 BMLoop * uvedit_loop_find_other_boundary_loop_with_visible_face(const Scene *scene, BMLoop *l_edge, BMVert *v_pivot, const BMUVOffsets &offsets)
static void uv_select_edgeloop_single_side_tag(const Scene *scene, BMesh *bm, BMLoop *l_init, const BMUVOffsets &offsets, enum eUVEdgeLoopBoundaryMode boundary_mode, int r_count_by_select[2])
static int uv_select_faceloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
static void uvedit_vertex_select_tagged(BMesh *bm, const Scene *scene, bool select)
void uvedit_select_prepare_custom_data(const Scene *scene, BMesh *bm)
static void uv_select_linked_multi(const Scene *scene, const Span< Object * > objects, UvNearestHit *hit, const bool extend, bool deselect, const bool toggle, const bool select_faces, const char hflag)
static wmOperatorStatus uv_select_edge_ring_exec(bContext *C, wmOperator *op)
UvNearestHit uv_nearest_hit_init_dist_px(const View2D *v2d, const float dist_px)
static wmOperatorStatus uv_select_all_exec(bContext *C, wmOperator *op)
void uvedit_face_select_disable(const Scene *scene, BMesh *bm, BMFace *efa)
static wmOperatorStatus uv_select_less_exec(bContext *C, wmOperator *)
static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMesh *bm)
void uvedit_vert_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMLoop *l, bool select)
static wmOperatorStatus uv_select_similar_vert_exec(bContext *C, wmOperator *op)
bool uvedit_face_select_test(const Scene *scene, const BMesh *bm, const BMFace *efa)
static void uvedit_sync_uvselect_flush_from_v3d(const ToolSettings *ts, BMesh *bm)
bool uvedit_face_visible_test_ex(const ToolSettings *ts, const BMFace *efa)
static bool UNUSED_FUNCTION bm_loop_select_vert_check_internal(const Scene *scene, BMesh *bm, BMLoop *l)
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])
static wmOperatorStatus uv_select_similar_exec(bContext *C, wmOperator *op)
static wmOperatorStatus uv_select_linked_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)
static void uv_select_all_perform_multi(const Scene *scene, Span< Object * > objects, int action)
static wmOperatorStatus uv_select_loop_exec(bContext *C, wmOperator *op)
void ED_uvedit_active_vert_loop_set(BMesh *bm, BMLoop *l)
static void uv_select_toggle_all(const Scene *scene, BMEditMesh *em)
static wmOperatorStatus uv_select_overlap(bContext *C, const bool extend)
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:4237
bool WM_gesture_is_modal_first(const wmGesture *gesture)
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
wmOperatorStatus WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus 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)
wmOperatorStatus WM_operator_flag_only_pass_through_on_press(wmOperatorStatus retval, const wmEvent *event)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:145