Blender V4.5
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
34#include "BLT_translation.hh"
35
36#include "BKE_context.hh"
37#include "BKE_customdata.hh"
38#include "BKE_editmesh.hh"
39#include "BKE_layer.hh"
40#include "BKE_material.hh"
41#include "BKE_mesh.hh"
42#include "BKE_mesh_mapping.hh"
43#include "BKE_report.hh"
44
45#include "DEG_depsgraph.hh"
47
48#include "ED_image.hh"
49#include "ED_mesh.hh"
50#include "ED_screen.hh"
51#include "ED_select_utils.hh"
52#include "ED_uvedit.hh"
53
54#include "RNA_access.hh"
55#include "RNA_define.hh"
56#include "RNA_enum_types.hh"
57
58#include "WM_api.hh"
59#include "WM_types.hh"
60
61#include "UI_view2d.hh"
62
63#include "uvedit_intern.hh"
64
65using blender::Array;
66using blender::int2;
67using blender::Span;
68using blender::Vector;
69
70static void uv_select_all_perform(const Scene *scene, Object *obedit, int action);
71
72static void uv_select_all_perform_multi_ex(const Scene *scene,
73 Span<Object *> objects,
74 int action,
75 const Object *ob_exclude);
76static void uv_select_all_perform_multi(const Scene *scene, Span<Object *> objects, int action);
77
78static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select);
79static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select);
80static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMesh *bm);
81
82static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
83 const ToolSettings *ts,
84 Object *obedit);
85
98
99/* -------------------------------------------------------------------- */
105
114
116{
117 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
118 if (ese && ese->prev) {
119 BMEditSelection *ese_prev = ese->prev;
120 if ((ese->htype == BM_VERT) && (ese_prev->htype == BM_FACE)) {
121 /* May be null. */
122 return BM_face_vert_share_loop((BMFace *)ese_prev->ele, (BMVert *)ese->ele);
123 }
124 }
125 return nullptr;
126}
127
136
138{
139 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
140 if (ese && ese->prev) {
141 BMEditSelection *ese_prev = ese->prev;
142 if ((ese->htype == BM_EDGE) && (ese_prev->htype == BM_FACE)) {
143 /* May be null. */
144 return BM_face_edge_share_loop((BMFace *)ese_prev->ele, (BMEdge *)ese->ele);
145 }
146 }
147 return nullptr;
148}
149
151
152/* -------------------------------------------------------------------- */
155
157{
158 const ToolSettings *ts = scene->toolsettings;
159 char uv_selectmode = UV_SELECT_VERTEX;
160
161 if (ts->uv_flag & UV_SYNC_SELECTION) {
162 if (ts->selectmode & SCE_SELECT_VERTEX) {
163 uv_selectmode = UV_SELECT_VERTEX;
164 }
165 else if (ts->selectmode & SCE_SELECT_EDGE) {
166 uv_selectmode = UV_SELECT_EDGE;
167 }
168 else if (ts->selectmode & SCE_SELECT_FACE) {
169 uv_selectmode = UV_SELECT_FACE;
170 }
171 }
172 else {
174 uv_selectmode = UV_SELECT_VERTEX;
175 }
176 else if (ts->uv_selectmode & UV_SELECT_EDGE) {
177 uv_selectmode = UV_SELECT_EDGE;
178 }
179 else if (ts->uv_selectmode & UV_SELECT_FACE) {
180 uv_selectmode = UV_SELECT_FACE;
181 }
182 }
183 return uv_selectmode;
184}
185
187{
188 /* bmesh API handles flushing but not on de-select */
189 if (ts->uv_flag & UV_SYNC_SELECTION) {
190 if (ts->selectmode != SCE_SELECT_FACE) {
191 if (select == false) {
193 }
194 else {
195 if (ts->selectmode & SCE_SELECT_VERTEX) {
197 }
198 else {
199 /* Use instead of #BM_mesh_select_flush so selecting edges doesn't
200 * flush vertex to face selection, see: #117320. */
202 }
203 }
204 }
205
206 if (select == false) {
208 }
209 }
210}
211
213 Scene *scene,
214 bool select,
215 const BMUVOffsets &offsets)
216{
217 BMFace *efa;
218 BMLoop *l;
219 BMIter iter, liter;
220
221 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
222 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
224 uvedit_uv_select_set(scene, bm, l, select, offsets);
225 }
226 }
227 }
228}
229
231{
232 if (ts->uv_flag & UV_SYNC_SELECTION) {
233 return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0);
234 }
236}
237bool uvedit_face_visible_test(const Scene *scene, const BMFace *efa)
238{
239 return uvedit_face_visible_test_ex(scene->toolsettings, efa);
240}
241
243 const BMFace *efa,
244 const BMUVOffsets &offsets)
245{
246 BLI_assert(offsets.select_vert >= 0);
247 BLI_assert(offsets.select_edge >= 0);
248 if (ts->uv_flag & UV_SYNC_SELECTION) {
250 }
251
252 const int cd_offset = (ts->uv_selectmode & UV_SELECT_VERTEX) ? offsets.select_vert :
253 offsets.select_edge;
254 const BMLoop *l_first = BM_FACE_FIRST_LOOP(efa);
255 const BMLoop *l_iter = l_first;
256 do {
257 if (!BM_ELEM_CD_GET_BOOL(l_iter, cd_offset)) {
258 return false;
259 }
260 } while ((l_iter = l_iter->next) != l_first);
261 return true;
262}
263bool uvedit_face_select_test(const Scene *scene, const BMFace *efa, const BMUVOffsets &offsets)
264{
265 return uvedit_face_select_test_ex(scene->toolsettings, efa, offsets);
266}
267
269 const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const BMUVOffsets &offsets)
270{
271 const ToolSettings *ts = scene->toolsettings;
272 const char sticky = ts->uv_sticky;
273 if (ts->uv_flag & UV_SYNC_SELECTION) {
274 uvedit_face_select_set(scene, bm, efa, select, offsets);
275 return;
276 }
277 if (!uvedit_face_visible_test(scene, efa)) {
278 return;
279 }
280 /* NOTE: Previously face selections done in sticky vertex mode selected stray UV vertices
281 * (not part of any face selections). This now uses the sticky location mode logic instead. */
282 switch (sticky) {
283 case SI_STICKY_DISABLE: {
284 uvedit_face_select_set(scene, bm, efa, select, offsets);
285 break;
286 }
287 default: {
288 /* SI_STICKY_LOC and SI_STICKY_VERTEX modes. */
289 uvedit_face_select_shared_vert(scene, bm, efa, select, offsets);
290 }
291 }
292}
293
295 const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const BMUVOffsets &offsets)
296{
297 BMLoop *l;
298 BMIter liter;
299
300 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
301 if (select) {
302 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
304 }
305 else {
306 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
307 if (!uvedit_vert_is_face_select_any_other(scene->toolsettings, l, offsets)) {
309 }
310 }
311 }
312}
313
315 const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const BMUVOffsets &offsets)
316{
317 if (select) {
318 uvedit_face_select_enable(scene, bm, efa, offsets);
319 }
320 else {
321 uvedit_face_select_disable(scene, bm, efa, offsets);
322 }
323}
324
326 BMesh *bm,
327 BMFace *efa,
328 const BMUVOffsets &offsets)
329{
330 BLI_assert(offsets.select_vert >= 0);
331 BLI_assert(offsets.select_edge >= 0);
332 const ToolSettings *ts = scene->toolsettings;
333
334 if (ts->uv_flag & UV_SYNC_SELECTION) {
335 BM_face_select_set(bm, efa, true);
336 }
337 else {
338 BMLoop *l;
339 BMIter liter;
340
341 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
342 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
343 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
344 }
345 }
346}
347
349 BMesh *bm,
350 BMFace *efa,
351 const BMUVOffsets &offsets)
352{
353 BLI_assert(offsets.select_vert >= 0);
354 BLI_assert(offsets.select_edge >= 0);
355 const ToolSettings *ts = scene->toolsettings;
356
357 if (ts->uv_flag & UV_SYNC_SELECTION) {
358 BM_face_select_set(bm, efa, false);
359 }
360 else {
361 BMLoop *l;
362 BMIter liter;
363
364 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
365 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
366 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
367 }
368 }
369}
370
372 const BMLoop *l,
373 const BMUVOffsets &offsets)
374{
375 BLI_assert(offsets.select_vert >= 0);
376 BLI_assert(offsets.select_edge >= 0);
377 if (ts->uv_flag & UV_SYNC_SELECTION) {
378 if (ts->selectmode == SCE_SELECT_FACE) {
379 /* Face only is a special case that can respect sticky modes. */
380 switch (ts->uv_sticky) {
381 case SI_STICKY_LOC: {
383 return true;
384 }
385 if (uvedit_edge_is_face_select_any_other(ts, l, offsets)) {
386 return true;
387 }
388 return false;
389 }
390 case SI_STICKY_DISABLE: {
392 }
393 default: {
394 /* #SI_STICKY_VERTEX */
396 }
397 }
399 }
400
403 }
406 }
407 return BM_elem_flag_test(l->v, BM_ELEM_SELECT) &&
409 }
410
412 return BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) &&
413 BM_ELEM_CD_GET_BOOL(l->next, offsets.select_vert);
414 }
415 return BM_ELEM_CD_GET_BOOL(l, offsets.select_edge);
416}
417
418bool uvedit_edge_select_test(const Scene *scene, const BMLoop *l, const BMUVOffsets &offsets)
419{
420 return uvedit_edge_select_test_ex(scene->toolsettings, l, offsets);
421}
422
424 const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
425{
426 const ToolSettings *ts = scene->toolsettings;
427 if (ts->uv_flag & UV_SYNC_SELECTION) {
428 uvedit_edge_select_set(scene, bm, l, select, offsets);
429 return;
430 }
431
432 const int sticky = ts->uv_sticky;
433 switch (sticky) {
434 case SI_STICKY_DISABLE: {
435 if (uvedit_face_visible_test(scene, l->f)) {
436 uvedit_edge_select_set(scene, bm, l, select, offsets);
437 }
438 break;
439 }
440 case SI_STICKY_VERTEX: {
442 break;
443 }
444 default: {
445 /* SI_STICKY_LOC (Fallback) */
447 break;
448 }
449 }
450}
451
453 BMesh *bm,
454 BMLoop *l,
455 const bool select,
456 const int sticky_flag,
457 const BMUVOffsets &offsets)
458{
460 /* Set edge flags. Rely on this for face visibility checks */
461 uvedit_edge_select_set_noflush(scene, l, select, sticky_flag, offsets);
462
463 /* Vert selections. */
464 BMLoop *l_iter = l;
465 do {
466 if (select) {
467 if (BM_ELEM_CD_GET_BOOL(l_iter, offsets.select_edge)) {
468 uvedit_uv_select_shared_vert(scene, bm, l_iter, true, SI_STICKY_LOC, offsets);
469 uvedit_uv_select_shared_vert(scene, bm, l_iter->next, true, SI_STICKY_LOC, offsets);
470 }
471 }
472 else {
473 if (!BM_ELEM_CD_GET_BOOL(l_iter, offsets.select_edge)) {
474 if (!uvedit_vert_is_edge_select_any_other(scene->toolsettings, l, offsets)) {
475 uvedit_uv_select_shared_vert(scene, bm, l_iter, false, SI_STICKY_LOC, offsets);
476 }
477 if (!uvedit_vert_is_edge_select_any_other(scene->toolsettings, l->next, offsets)) {
478 uvedit_uv_select_shared_vert(scene, bm, l_iter->next, false, SI_STICKY_LOC, offsets);
479 }
480 }
481 }
482
483 } while (((l_iter = l_iter->radial_next) != l) && (sticky_flag != SI_STICKY_LOC));
484}
485
487 BMLoop *l,
488 const bool select,
489 const int sticky_flag,
490 const BMUVOffsets &offsets)
491{
492 BLI_assert(offsets.uv >= 0);
493 BLI_assert(offsets.select_edge >= 0);
494 BMLoop *l_iter = l;
495 do {
496 if (uvedit_face_visible_test(scene, l_iter->f)) {
497 if ((sticky_flag == SI_STICKY_VERTEX) || BM_loop_uv_share_edge_check(l, l_iter, offsets.uv))
498 {
499 BM_ELEM_CD_SET_BOOL(l_iter, offsets.select_edge, select);
500 }
501 }
502 } while (((l_iter = l_iter->radial_next) != l) && (sticky_flag != SI_STICKY_DISABLE));
503}
504
506 const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
507{
508 if (select) {
509 uvedit_edge_select_enable(scene, bm, l, offsets);
510 }
511 else {
512 uvedit_edge_select_disable(scene, bm, l, offsets);
513 }
514}
515
517 BMesh *bm,
518 BMLoop *l,
519 const BMUVOffsets &offsets)
520
521{
522 const ToolSettings *ts = scene->toolsettings;
523 BLI_assert(offsets.select_vert >= 0);
524 BLI_assert(offsets.select_edge >= 0);
525
526 if (ts->uv_flag & UV_SYNC_SELECTION) {
527 if (ts->selectmode & SCE_SELECT_FACE) {
528 BM_face_select_set(bm, l->f, true);
529 }
530 else if (ts->selectmode & SCE_SELECT_EDGE) {
531 BM_edge_select_set(bm, l->e, true);
532 }
533 else {
534 BM_vert_select_set(bm, l->e->v1, true);
535 BM_vert_select_set(bm, l->e->v2, true);
536 }
537 }
538 else {
539 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
540 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
541 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, true);
542 }
543}
544
546 BMesh *bm,
547 BMLoop *l,
548 const BMUVOffsets &offsets)
549{
550 const ToolSettings *ts = scene->toolsettings;
551 BLI_assert(offsets.select_vert >= 0);
552 BLI_assert(offsets.select_edge >= 0);
553
554 if (ts->uv_flag & UV_SYNC_SELECTION) {
555 if (ts->selectmode & SCE_SELECT_FACE) {
556 BM_face_select_set(bm, l->f, false);
557 }
558 else if (ts->selectmode & SCE_SELECT_EDGE) {
559 BM_edge_select_set(bm, l->e, false);
560 }
561 else {
562 BM_vert_select_set(bm, l->e->v1, false);
563 BM_vert_select_set(bm, l->e->v2, false);
564 }
565 }
566 else {
567 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
568 if ((ts->uv_selectmode & UV_SELECT_VERTEX) == 0) {
569 /* Deselect UV vertex if not part of another edge selection */
570 if (!BM_ELEM_CD_GET_BOOL(l->next, offsets.select_edge)) {
571 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, false);
572 }
573 if (!BM_ELEM_CD_GET_BOOL(l->prev, offsets.select_edge)) {
574 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
575 }
576 }
577 else {
578 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
579 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, false);
580 }
581 }
582}
583
584bool uvedit_uv_select_test_ex(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets &offsets)
585{
586 BLI_assert(offsets.select_vert >= 0);
587 if (ts->uv_flag & UV_SYNC_SELECTION) {
588
589 if (ts->selectmode == SCE_SELECT_FACE) {
590 /* Face only is a special case that can respect sticky modes. */
591 switch (ts->uv_sticky) {
592 case SI_STICKY_LOC: {
594 return true;
595 }
596 if (uvedit_vert_is_face_select_any_other(ts, l, offsets)) {
597 return true;
598 }
599 return false;
600 }
601 case SI_STICKY_DISABLE: {
603 }
604 default: {
605 /* #SI_STICKY_VERTEX */
607 }
608 }
610 }
611
613 /* Edge/Face is a special case that can respect sticky modes. */
614 switch (ts->uv_sticky) {
615 case SI_STICKY_LOC: {
617 return true;
618 }
619 if (uvedit_vert_is_edge_select_any_other(ts, l, offsets)) {
620 return true;
621 }
622 return false;
623 }
624 case SI_STICKY_DISABLE: {
625 return BM_elem_flag_test(l->e, BM_ELEM_SELECT) ||
627 }
628 default: {
629 /* #SI_STICKY_VERTEX */
631 }
632 }
634 }
635
636 if (ts->selectmode & SCE_SELECT_FACE) {
638 }
639 if (ts->selectmode & SCE_SELECT_EDGE) {
640 /* Are you looking for `uvedit_edge_select_test(...)` instead? */
641 }
643 }
644
645 if (ts->selectmode & SCE_SELECT_FACE) {
646 /* Are you looking for `uvedit_face_select_test(...)` instead? */
647 }
648
649 if (ts->selectmode & SCE_SELECT_EDGE) {
650 /* Are you looking for `uvedit_edge_select_test(...)` instead? */
651 }
652
653 return BM_ELEM_CD_GET_BOOL(l, offsets.select_vert);
654}
655
656bool uvedit_uv_select_test(const Scene *scene, const BMLoop *l, const BMUVOffsets &offsets)
657{
658 return uvedit_uv_select_test_ex(scene->toolsettings, l, offsets);
659}
660
662 const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
663{
664 const ToolSettings *ts = scene->toolsettings;
665 if (ts->uv_flag & UV_SYNC_SELECTION) {
666 uvedit_uv_select_set(scene, bm, l, select, offsets);
667 return;
668 }
669
670 const int sticky = ts->uv_sticky;
671 switch (sticky) {
672 case SI_STICKY_DISABLE: {
673 if (uvedit_face_visible_test(scene, l->f)) {
674 uvedit_uv_select_set(scene, bm, l, select, offsets);
675 }
676 break;
677 }
678 case SI_STICKY_VERTEX: {
680 break;
681 }
682 default: {
683 /* SI_STICKY_LOC. */
685 break;
686 }
687 }
688}
689
691 BMesh *bm,
692 BMLoop *l,
693 const bool select,
694 const int sticky_flag,
695 const BMUVOffsets &offsets)
696{
698 BLI_assert(offsets.uv >= 0);
699
700 BMEdge *e_first, *e_iter;
701 e_first = e_iter = l->e;
702 do {
703 BMLoop *l_radial_iter = e_iter->l;
704 if (!l_radial_iter) {
705 continue; /* Skip wire edges with no loops. */
706 }
707 do {
708 if (l_radial_iter->v == l->v) {
709 if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
710 bool do_select = false;
711 if (sticky_flag == SI_STICKY_VERTEX) {
712 do_select = true;
713 }
714 else if (BM_loop_uv_share_vert_check(l, l_radial_iter, offsets.uv)) {
715 do_select = true;
716 }
717
718 if (do_select) {
719 uvedit_uv_select_set(scene, bm, l_radial_iter, select, offsets);
720 }
721 }
722 }
723 } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
724 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
725}
726
728 const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
729{
730 if (select) {
731 uvedit_uv_select_enable(scene, bm, l, offsets);
732 }
733 else {
734 uvedit_uv_select_disable(scene, bm, l, offsets);
735 }
736}
737
738void uvedit_uv_select_enable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets &offsets)
739{
740 const ToolSettings *ts = scene->toolsettings;
741 BLI_assert(offsets.select_vert >= 0);
742
743 if (ts->selectmode & SCE_SELECT_EDGE) {
744 /* Are you looking for `uvedit_edge_select_set(...)` instead? */
745 }
746
747 if (ts->uv_flag & UV_SYNC_SELECTION) {
748 if (ts->selectmode & SCE_SELECT_FACE) {
749 BM_face_select_set(bm, l->f, true);
750 }
751 else {
752 BM_vert_select_set(bm, l->v, true);
753 }
754 }
755 else {
756 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
757 }
758}
759
760void uvedit_uv_select_disable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets &offsets)
761{
762 const ToolSettings *ts = scene->toolsettings;
763 BLI_assert(offsets.select_vert >= 0);
764
765 if (ts->uv_flag & UV_SYNC_SELECTION) {
766 if (ts->selectmode & SCE_SELECT_FACE) {
767 BM_face_select_set(bm, l->f, false);
768 }
769 else {
770 BM_vert_select_set(bm, l->v, false);
771 }
772 }
773 else {
774 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
775 }
776}
777
779 BMLoop *l_src,
780 const BMUVOffsets &offsets)
781{
782 BLI_assert(offsets.uv >= 0);
783 BMLoop *l_other = nullptr;
784 BMLoop *l_iter = l_src->radial_next;
785 if (l_iter != l_src) {
786 do {
787 if (uvedit_face_visible_test(scene, l_iter->f) &&
788 BM_loop_uv_share_edge_check(l_src, l_iter, offsets.uv))
789 {
790 /* Check UVs are contiguous. */
791 if (l_other == nullptr) {
792 l_other = l_iter;
793 }
794 else {
795 /* Only use when there is a single alternative. */
796 l_other = nullptr;
797 break;
798 }
799 }
800 } while ((l_iter = l_iter->radial_next) != l_src);
801 }
802 return l_other;
803}
804
806 BMLoop *l_edge,
807 BMVert *v_pivot,
808 const BMUVOffsets &offsets)
809{
811 nullptr);
812
813 BMLoop *l_step = l_edge;
814 l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
815 BMLoop *l_step_last = nullptr;
816 do {
817 BLI_assert(BM_vert_in_edge(l_step->e, v_pivot));
818 l_step_last = l_step;
819 l_step = uvedit_loop_find_other_radial_loop_with_visible_face(scene, l_step, offsets);
820 if (l_step) {
821 l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
822 }
823 } while (l_step != nullptr);
824
825 if (l_step_last != nullptr) {
827 nullptr);
828 }
829
830 return l_step_last;
831}
832
834
835/* -------------------------------------------------------------------- */
838
839UvNearestHit uv_nearest_hit_init_dist_px(const View2D *v2d, const float dist_px)
840{
841 UvNearestHit hit = {nullptr};
842 hit.dist_sq = square_f(U.pixelsize * dist_px);
843 hit.scale[0] = UI_view2d_scale_get_x(v2d);
844 hit.scale[1] = UI_view2d_scale_get_y(v2d);
845 return hit;
846}
847
849{
850 UvNearestHit hit = {nullptr};
851 hit.dist_sq = FLT_MAX;
852 hit.scale[0] = UI_view2d_scale_get_x(v2d);
853 hit.scale[1] = UI_view2d_scale_get_y(v2d);
854 return hit;
855}
856
858{
859 UvNearestHit hit = {nullptr};
860 hit.dist_sq = FLT_MAX;
861 hit.scale[0] = 1.0f;
862 hit.scale[1] = 1.0f;
863 return hit;
864}
865
867 Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
868{
869 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
871 BMFace *efa;
872 BMLoop *l;
873 BMIter iter, liter;
874 float *luv, *luv_next;
875 int i;
876 bool found = false;
877
878 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
879 BLI_assert(offsets.uv >= 0);
880
882
883 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
884 if (!uvedit_face_visible_test(scene, efa)) {
885 continue;
886 }
887 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
888 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
889 luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, offsets.uv);
890
891 float delta[2];
892 closest_to_line_segment_v2(delta, co, luv, luv_next);
893
894 sub_v2_v2(delta, co);
895 mul_v2_v2(delta, hit->scale);
896
897 float dist_test_sq = len_squared_v2(delta);
898
899 /* Ensures that successive selection attempts will select other edges sharing the same
900 * UV coordinates as the previous selection. */
901 if ((penalty != 0.0f) && uvedit_edge_select_test(scene, l, offsets)) {
902 dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty);
903 }
904 if (dist_test_sq < hit->dist_sq) {
905 hit->ob = obedit;
906 hit->efa = efa;
907
908 hit->l = l;
909
910 hit->dist_sq = dist_test_sq;
911 found = true;
912 }
913 }
914 }
915 return found;
916}
917
919 const Span<Object *> objects,
920 const float co[2],
921 const float penalty,
922 UvNearestHit *hit)
923{
924 bool found = false;
925 for (Object *obedit : objects) {
926 if (uv_find_nearest_edge(scene, obedit, co, penalty, hit)) {
927 found = true;
928 }
929 }
930 return found;
931}
932
934 Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit, const bool only_in_face)
935{
936 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
938 bool found = false;
939
940 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
941
942 BMIter iter;
943 BMFace *efa;
944
945 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
946 if (!uvedit_face_visible_test(scene, efa)) {
947 continue;
948 }
949
950 float cent[2];
951 BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent);
952
953 float delta[2];
954 sub_v2_v2v2(delta, co, cent);
955 mul_v2_v2(delta, hit->scale);
956
957 const float dist_test_sq = len_squared_v2(delta);
958
959 if (dist_test_sq < hit->dist_sq) {
960
961 if (only_in_face) {
962 if (!BM_face_uv_point_inside_test(efa, co, cd_loop_uv_offset)) {
963 continue;
964 }
965 }
966
967 hit->ob = obedit;
968 hit->efa = efa;
969 hit->dist_sq = dist_test_sq;
970 found = true;
971 }
972 }
973 return found;
974}
975
976bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit)
977{
978 return uv_find_nearest_face_ex(scene, obedit, co, hit, false);
979}
980
982 const Span<Object *> objects,
983 const float co[2],
984 UvNearestHit *hit,
985 const bool only_in_face)
986{
987 bool found = false;
988 for (Object *obedit : objects) {
989 if (uv_find_nearest_face_ex(scene, obedit, co, hit, only_in_face)) {
990 found = true;
991 }
992 }
993 return found;
994}
995
997 const Span<Object *> objects,
998 const float co[2],
999 UvNearestHit *hit)
1000{
1001 return uv_find_nearest_face_multi_ex(scene, objects, co, hit, false);
1002}
1003
1004static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset)
1005{
1006 const float *uv_prev = BM_ELEM_CD_GET_FLOAT_P(l->prev, cd_loop_uv_offset);
1007 const float *uv_curr = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1008 const float *uv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
1009
1010 return ((line_point_side_v2(uv_prev, uv_curr, co) > 0.0f) &&
1011 (line_point_side_v2(uv_next, uv_curr, co) <= 0.0f));
1012}
1013
1015 Scene *scene, Object *obedit, float const co[2], const float penalty_dist, UvNearestHit *hit)
1016{
1017 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
1018 bool found = false;
1019
1020 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1021 BMFace *efa;
1022 BMIter iter;
1023
1025
1026 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1027 BLI_assert(offsets.uv >= 0);
1028
1029 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1030 if (!uvedit_face_visible_test(scene, efa)) {
1031 continue;
1032 }
1033
1034 BMIter liter;
1035 BMLoop *l;
1036 int i;
1037 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1038 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
1039
1040 float delta[2];
1041
1042 sub_v2_v2v2(delta, co, luv);
1043 mul_v2_v2(delta, hit->scale);
1044
1045 float dist_test_sq = len_squared_v2(delta);
1046
1047 /* Ensures that successive selection attempts will select other vertices sharing the same
1048 * UV coordinates */
1049 if ((penalty_dist != 0.0f) && uvedit_uv_select_test(scene, l, offsets)) {
1050 dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty_dist);
1051 }
1052
1053 if (dist_test_sq <= hit->dist_sq) {
1054 if (dist_test_sq == hit->dist_sq) {
1055 if (!uv_nearest_between(l, co, offsets.uv)) {
1056 continue;
1057 }
1058 }
1059
1060 hit->dist_sq = dist_test_sq;
1061
1062 hit->ob = obedit;
1063 hit->efa = efa;
1064 hit->l = l;
1065 found = true;
1066 }
1067 }
1068 }
1069
1070 return found;
1071}
1072
1074 const Span<Object *> objects,
1075 float const co[2],
1076 const float penalty_dist,
1077 UvNearestHit *hit)
1078{
1079 bool found = false;
1080 for (Object *obedit : objects) {
1081 if (uv_find_nearest_vert(scene, obedit, co, penalty_dist, hit)) {
1082 found = true;
1083 }
1084 }
1085 return found;
1086}
1087
1088static bool uvedit_nearest_uv(const Scene *scene,
1089 Object *obedit,
1090 const float co[2],
1091 const float scale[2],
1092 const bool ignore_selected,
1093 float *dist_sq,
1094 float r_uv[2])
1095{
1096 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1097 BMIter iter;
1098 BMFace *efa;
1099 const float *uv_best = nullptr;
1100 float dist_best = *dist_sq;
1101 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1102 BLI_assert(offsets.uv >= 0);
1103 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1104 if (!uvedit_face_visible_test(scene, efa)) {
1105 continue;
1106 }
1107 BMLoop *l_iter, *l_first;
1108 l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
1109 do {
1110 if (ignore_selected && uvedit_uv_select_test(scene, l_iter, offsets)) {
1111 continue;
1112 }
1113
1114 const float *uv = BM_ELEM_CD_GET_FLOAT_P(l_iter, offsets.uv);
1115 float co_tmp[2];
1116 mul_v2_v2v2(co_tmp, scale, uv);
1117 const float dist_test = len_squared_v2v2(co, co_tmp);
1118 if (dist_best > dist_test) {
1119 dist_best = dist_test;
1120 uv_best = uv;
1121 }
1122 } while ((l_iter = l_iter->next) != l_first);
1123 }
1124
1125 if (uv_best != nullptr) {
1126 copy_v2_v2(r_uv, uv_best);
1127 *dist_sq = dist_best;
1128 return true;
1129 }
1130 return false;
1131}
1132
1134 const Scene *scene,
1135 const Span<Object *> objects,
1136 const float mval_fl[2],
1137 const bool ignore_selected,
1138 float *dist_sq,
1139 float r_uv[2])
1140{
1141 bool found = false;
1142
1143 float scale[2], offset[2];
1144 UI_view2d_scale_get(v2d, &scale[0], &scale[1]);
1145 UI_view2d_view_to_region_fl(v2d, 0.0f, 0.0f, &offset[0], &offset[1]);
1146
1147 float co[2];
1148 sub_v2_v2v2(co, mval_fl, offset);
1149
1150 for (Object *obedit : objects) {
1151 if (uvedit_nearest_uv(scene, obedit, co, scale, ignore_selected, dist_sq, r_uv)) {
1152 found = true;
1153 }
1154 }
1155 return found;
1156}
1157
1159
1160/* -------------------------------------------------------------------- */
1167
1168BMLoop *uv_find_nearest_loop_from_vert(Scene *scene, Object *obedit, BMVert *v, const float co[2])
1169{
1170 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1171 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
1172
1173 BMIter liter;
1174 BMLoop *l;
1175 BMLoop *l_found = nullptr;
1176 float dist_best_sq = FLT_MAX;
1177
1178 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
1179 if (!uvedit_face_visible_test(scene, l->f)) {
1180 continue;
1181 }
1182
1183 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1184 const float dist_test_sq = len_squared_v2v2(co, luv);
1185 if (dist_test_sq < dist_best_sq) {
1186 dist_best_sq = dist_test_sq;
1187 l_found = l;
1188 }
1189 }
1190 return l_found;
1191}
1192
1193BMLoop *uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, const float co[2])
1194{
1195 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1196 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
1197
1198 BMIter eiter;
1199 BMLoop *l;
1200 BMLoop *l_found = nullptr;
1201 float dist_best_sq = FLT_MAX;
1202
1203 BM_ITER_ELEM (l, &eiter, e, BM_LOOPS_OF_EDGE) {
1204 if (!uvedit_face_visible_test(scene, l->f)) {
1205 continue;
1206 }
1207 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1208 const float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
1209 const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv, luv_next);
1210 if (dist_test_sq < dist_best_sq) {
1211 dist_best_sq = dist_test_sq;
1212 l_found = l;
1213 }
1214 }
1215 return l_found;
1216}
1217
1219
1220/* -------------------------------------------------------------------- */
1223
1225{
1226 const ToolSettings *ts = scene->toolsettings;
1227 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
1229 const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2);
1230 BM_uv_map_attr_vert_select_ensure(bm, active_uv_name);
1231 BM_uv_map_attr_edge_select_ensure(bm, active_uv_name);
1232}
1233
1235 const BMLoop *l,
1236 const BMUVOffsets &offsets)
1237{
1238 BLI_assert(offsets.uv >= 0);
1239 BMEdge *e_iter = l->e;
1240 do {
1241 BMLoop *l_radial_iter = e_iter->l;
1242 if (!l_radial_iter) {
1243 continue; /* Skip wire edges with no loops. */
1244 }
1245 do {
1246 if (!uvedit_face_visible_test_ex(ts, l_radial_iter->f)) {
1247 continue;
1248 }
1249 /* Use #l_other to check if the uvs are connected (share the same uv coordinates)
1250 * and #l_radial_iter for the actual edge selection test. */
1251 BMLoop *l_other = (l_radial_iter->v != l->v) ? l_radial_iter->next : l_radial_iter;
1252 if (BM_loop_uv_share_vert_check(l, l_other, offsets.uv) &&
1253 uvedit_edge_select_test_ex(ts, l_radial_iter, offsets))
1254 {
1255 return true;
1256 }
1257 } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
1258 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != l->e);
1259
1260 return false;
1261}
1262
1264 const BMLoop *l,
1265 const BMUVOffsets &offsets)
1266{
1267 BLI_assert(offsets.uv >= 0);
1268 BMLoop *l_radial_iter = l->radial_next;
1269 if (l_radial_iter == l) {
1270 return false;
1271 }
1272 do {
1273 if (!uvedit_face_visible_test_ex(ts, l_radial_iter->f)) {
1274 continue;
1275 }
1276 if (BM_loop_uv_share_edge_check(l, l_radial_iter, offsets.uv) &&
1277 uvedit_face_select_test_ex(ts, l_radial_iter->f, offsets))
1278 {
1279 return true;
1280 }
1281 } while ((l_radial_iter = l_radial_iter->radial_next) != l);
1282
1283 return false;
1284}
1285
1287 const BMLoop *l,
1288 const BMUVOffsets &offsets)
1289{
1290 BLI_assert(offsets.uv >= 0);
1291 BMIter liter;
1292 BMLoop *l_iter;
1293 BM_ITER_ELEM (l_iter, &liter, l->v, BM_LOOPS_OF_VERT) {
1294 if ((l_iter->f == l->f) || !uvedit_face_visible_test_ex(ts, l_iter->f)) {
1295 continue;
1296 }
1297 if (BM_loop_uv_share_vert_check(l, l_iter, offsets.uv) &&
1298 uvedit_face_select_test_ex(ts, l_iter->f, offsets))
1299 {
1300 return true;
1301 }
1302 }
1303 return false;
1304}
1305
1307 const BMLoop *l,
1308 const BMUVOffsets &offsets)
1309{
1310 BLI_assert(offsets.uv >= 0);
1311 BMIter liter;
1312 BMLoop *l_iter;
1313 BM_ITER_ELEM (l_iter, &liter, l->v, BM_LOOPS_OF_VERT) {
1314 if ((l_iter->f == l->f) || !uvedit_face_visible_test_ex(ts, l_iter->f)) {
1315 continue;
1316 }
1317 if (BM_loop_uv_share_vert_check(l, l_iter, offsets.uv) &&
1318 !uvedit_face_select_test_ex(ts, l_iter->f, offsets))
1319 {
1320 return false;
1321 }
1322 }
1323 return true;
1324}
1325
1326static void bm_clear_uv_vert_selection(const Scene *scene, BMesh *bm, const BMUVOffsets &offsets)
1327{
1328 if (offsets.select_vert == -1) {
1329 return;
1330 }
1331 BMFace *efa;
1332 BMLoop *l;
1333 BMIter iter, liter;
1334 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1335 if (!uvedit_face_visible_test(scene, efa)) {
1336 continue;
1337 }
1338 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1339 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
1340 }
1341 }
1342}
1343
1345
1346/* -------------------------------------------------------------------- */
1350
1352{
1353 const ToolSettings *ts = scene->toolsettings;
1354 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
1356
1358 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1359
1360 /* Vertex Mode only. */
1361 if (ts->uv_selectmode & UV_SELECT_VERTEX) {
1362 BMFace *efa;
1363 BMLoop *l;
1364 BMIter iter, liter;
1365 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1366 if (!uvedit_face_visible_test(scene, efa)) {
1367 continue;
1368 }
1369 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1370 bool edge_selected = BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) &&
1371 BM_ELEM_CD_GET_BOOL(l->next, offsets.select_vert);
1372 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, edge_selected);
1373 }
1374 }
1375 }
1376}
1377
1379
1380/* -------------------------------------------------------------------- */
1383
1384void uvedit_select_flush(const Scene *scene, BMesh *bm)
1385{
1386 /* Careful when using this in face select mode.
1387 * For face selections with sticky mode enabled, this can create invalid selection states. */
1388 const ToolSettings *ts = scene->toolsettings;
1389 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
1391
1393 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1394
1395 BMFace *efa;
1396 BMLoop *l;
1397 BMIter iter, liter;
1398 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1399 if (!uvedit_face_visible_test(scene, efa)) {
1400 continue;
1401 }
1402 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1403 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) &&
1404 BM_ELEM_CD_GET_BOOL(l->next, offsets.select_vert))
1405 {
1406 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
1407 }
1408 }
1409 }
1410}
1411
1413{
1414 const ToolSettings *ts = scene->toolsettings;
1415 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
1417
1419 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1420
1421 BMFace *efa;
1422 BMLoop *l;
1423 BMIter iter, liter;
1424 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1425 if (!uvedit_face_visible_test(scene, efa)) {
1426 continue;
1427 }
1428 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1429 if (!BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) ||
1430 !BM_ELEM_CD_GET_BOOL(l->next, offsets.select_vert))
1431 {
1432 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
1433 }
1434 }
1435 }
1436}
1437
1439
1440/* -------------------------------------------------------------------- */
1443
1451
1453 BMLoop *l_step,
1454 BMVert *v_from,
1455 const BMUVOffsets &offsets)
1456{
1457 if (l_step->f->len == 4) {
1458 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1459 BMLoop *l_step_over = (v_from == l_step->v) ? l_step->next : l_step->prev;
1461 scene, l_step_over, offsets);
1462 if (l_step_over) {
1463 return (l_step_over->v == v_from_next) ? l_step_over->prev : l_step_over->next;
1464 }
1465 }
1466 return nullptr;
1467}
1468
1470 BMLoop *l_step,
1471 BMVert *v_from,
1472 const BMUVOffsets &offsets)
1473{
1474 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1476 scene, l_step, v_from_next, offsets);
1477}
1478
1479/* TODO(@ideasman42): support this in the BMesh API, as we have for clearing other types. */
1481{
1482 BMIter iter;
1483 BMFace *f;
1484 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1485 BMIter liter;
1486 BMLoop *l_iter;
1487 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
1489 }
1490 }
1491}
1492
1497 BMesh *bm,
1498 BMLoop *l_init_pair[2],
1499 const BMUVOffsets &offsets)
1500{
1502
1503 for (int side = 0; side < 2; side++) {
1504 BMLoop *l_step_pair[2] = {l_init_pair[0], l_init_pair[1]};
1505 BMVert *v_from = side ? l_step_pair[0]->e->v1 : l_step_pair[0]->e->v2;
1506 /* Disable since we start from the same edge. */
1507 BM_elem_flag_disable(l_step_pair[0], BM_ELEM_TAG);
1508 BM_elem_flag_disable(l_step_pair[1], BM_ELEM_TAG);
1509 while ((l_step_pair[0] != nullptr) && (l_step_pair[1] != nullptr)) {
1510 if (!uvedit_face_visible_test(scene, l_step_pair[0]->f) ||
1511 !uvedit_face_visible_test(scene, l_step_pair[1]->f) ||
1512 /* Check loops have not diverged. */
1513 (uvedit_loop_find_other_radial_loop_with_visible_face(scene, l_step_pair[0], offsets) !=
1514 l_step_pair[1]))
1515 {
1516 break;
1517 }
1518
1519 BLI_assert(l_step_pair[0]->e == l_step_pair[1]->e);
1520
1521 BM_elem_flag_enable(l_step_pair[0], BM_ELEM_TAG);
1522 BM_elem_flag_enable(l_step_pair[1], BM_ELEM_TAG);
1523
1524 BMVert *v_from_next = BM_edge_other_vert(l_step_pair[0]->e, v_from);
1525 /* Walk over both sides, ensure they keep on the same edge. */
1526 for (int i = 0; i < ARRAY_SIZE(l_step_pair); i++) {
1528 scene, l_step_pair[i], v_from, offsets);
1529 }
1530
1531 if ((l_step_pair[0] && BM_elem_flag_test(l_step_pair[0], BM_ELEM_TAG)) ||
1532 (l_step_pair[1] && BM_elem_flag_test(l_step_pair[1], BM_ELEM_TAG)))
1533 {
1534 break;
1535 }
1536 v_from = v_from_next;
1537 }
1538 }
1539}
1540
1548 BMesh *bm,
1549 BMLoop *l_init,
1550 const BMUVOffsets &offsets,
1551 enum eUVEdgeLoopBoundaryMode boundary_mode,
1552 int r_count_by_select[2])
1553{
1554 if (r_count_by_select) {
1555 r_count_by_select[0] = r_count_by_select[1] = 0;
1556 }
1557
1559
1560 for (int side = 0; side < 2; side++) {
1561 BMLoop *l_step = l_init;
1562 BMVert *v_from = side ? l_step->e->v1 : l_step->e->v2;
1563 /* Disable since we start from the same edge. */
1565 while (l_step != nullptr) {
1566
1567 if (!uvedit_face_visible_test(scene, l_step->f) ||
1568 /* Check the boundary is still a boundary. */
1570 nullptr))
1571 {
1572 break;
1573 }
1574
1575 if (r_count_by_select != nullptr) {
1576 r_count_by_select[uvedit_edge_select_test(scene, l_step, offsets)] += 1;
1577 /* Early exit when mixed could be optional if needed. */
1578 if (r_count_by_select[0] && r_count_by_select[1]) {
1579 r_count_by_select[0] = r_count_by_select[1] = -1;
1580 break;
1581 }
1582 }
1583
1585
1586 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1587 BMFace *f_step_prev = l_step->f;
1588
1589 l_step = bm_select_edgeloop_single_side_next(scene, l_step, v_from, offsets);
1590
1591 if (l_step && BM_elem_flag_test(l_step, BM_ELEM_TAG)) {
1592 break;
1593 }
1594 if (boundary_mode == UV_EDGE_LOOP_BOUNDARY_LOOP) {
1595 /* Don't allow walking over the face. */
1596 if (f_step_prev == l_step->f) {
1597 break;
1598 }
1599 }
1600 v_from = v_from_next;
1601 }
1602 }
1603}
1604
1605static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
1606{
1607 const ToolSettings *ts = scene->toolsettings;
1608 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1609 bool select;
1610
1611 /* NOTE: this is a special case, even when sync select is enabled,
1612 * the flags are used then flushed to the vertices.
1613 * So these need to be ensured even though the layers aren't used afterwards. */
1614 const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2);
1615 BM_uv_map_attr_vert_select_ensure(bm, active_uv_name);
1616 BM_uv_map_attr_edge_select_ensure(bm, active_uv_name);
1617 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1618
1619 if (extend) {
1620 select = !uvedit_edge_select_test(scene, hit->l, offsets);
1621 }
1622 else {
1623 select = true;
1624 }
1625
1626 BMLoop *l_init_pair[2] = {
1627 hit->l,
1629 };
1630
1631 /* When selecting boundaries, support cycling between selection modes. */
1633
1634 /* Tag all loops that are part of the edge loop (select after).
1635 * This is done so we can */
1636 if (l_init_pair[1] == nullptr) {
1637 int count_by_select[2];
1638 /* If the loops selected toggle the boundaries. */
1640 scene, bm, l_init_pair[0], offsets, boundary_mode, count_by_select);
1641 if (count_by_select[!select] == 0) {
1642 boundary_mode = UV_EDGE_LOOP_BOUNDARY_ALL;
1643
1644 /* If the boundary is selected, toggle back to the loop. */
1646 scene, bm, l_init_pair[0], offsets, boundary_mode, count_by_select);
1647 if (count_by_select[!select] == 0) {
1648 boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP;
1649 }
1650 }
1651 }
1652
1653 if (l_init_pair[1] == nullptr) {
1654 uv_select_edgeloop_single_side_tag(scene, bm, l_init_pair[0], offsets, boundary_mode, nullptr);
1655 }
1656 else {
1657 uv_select_edgeloop_double_side_tag(scene, bm, l_init_pair, offsets);
1658 }
1659
1660 /* Apply the selection. */
1661 if (!extend) {
1662 uv_select_all_perform(scene, obedit, SEL_DESELECT);
1663 }
1664
1665 /* Select all tagged loops. */
1666 {
1667 BMIter iter;
1668 BMFace *f;
1669 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1670 BMIter liter;
1671 BMLoop *l_iter;
1672 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
1673 if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
1674 if (ts->uv_selectmode == UV_SELECT_VERTEX) {
1675 uvedit_uv_select_set_with_sticky(scene, bm, l_iter, select, offsets);
1676 uvedit_uv_select_set_with_sticky(scene, bm, l_iter->next, select, offsets);
1677 }
1678 else {
1679 uvedit_edge_select_set_with_sticky(scene, bm, l_iter, select, offsets);
1680 }
1681 }
1682 }
1683 }
1684 }
1685
1686 return select ? 1 : -1;
1687}
1688
1690
1691/* -------------------------------------------------------------------- */
1694
1695static int uv_select_faceloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
1696{
1697 const ToolSettings *ts = scene->toolsettings;
1698 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1699 bool select;
1700
1701 if (ts->uv_flag & UV_SYNC_SELECTION) {
1702 /* Pass. */
1703 }
1704 else {
1706 }
1707 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1708
1709 if (!extend) {
1710 uv_select_all_perform(scene, obedit, SEL_DESELECT);
1711 }
1712
1714
1715 if (extend) {
1716 select = !uvedit_face_select_test(scene, hit->l->f, offsets);
1717 }
1718 else {
1719 select = true;
1720 }
1721
1722 BMLoop *l_pair[2] = {
1723 hit->l,
1725 };
1726
1727 for (int side = 0; side < 2; side++) {
1728 BMLoop *l_step = l_pair[side];
1729 while (l_step) {
1730 if (!uvedit_face_visible_test(scene, l_step->f)) {
1731 break;
1732 }
1733
1734 uvedit_face_select_set_with_sticky(scene, bm, l_step->f, select, offsets);
1735
1737 if (l_step->f->len == 4) {
1738 BMLoop *l_step_opposite = l_step->next->next;
1740 scene, l_step_opposite, offsets);
1741 }
1742 else {
1743 l_step = nullptr;
1744 }
1745
1746 /* Break iteration when `l_step`:
1747 * - is the first loop where we started from.
1748 * - tagged using #BM_ELEM_TAG (meaning this loop has been visited in this iteration). */
1749 if (l_step && BM_elem_flag_test(l_step->f, BM_ELEM_TAG)) {
1750 break;
1751 }
1752 }
1753 }
1754
1755 return (select) ? 1 : -1;
1756}
1757
1759
1760/* -------------------------------------------------------------------- */
1763
1764static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
1765{
1766 const ToolSettings *ts = scene->toolsettings;
1767 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1768 const bool use_face_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
1769 (ts->selectmode & SCE_SELECT_FACE) :
1771 const bool use_vertex_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
1774 bool select;
1775
1776 if (ts->uv_flag & UV_SYNC_SELECTION) {
1777 /* Pass. */
1778 }
1779 else {
1781 }
1782 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1783
1784 if (!extend) {
1785 uv_select_all_perform(scene, obedit, SEL_DESELECT);
1786 }
1787
1789
1790 if (extend) {
1791 select = !uvedit_edge_select_test(scene, hit->l, offsets);
1792 }
1793 else {
1794 select = true;
1795 }
1796
1797 BMLoop *l_pair[2] = {
1798 hit->l,
1800 };
1801
1802 for (int side = 0; side < 2; side++) {
1803 BMLoop *l_step = l_pair[side];
1804 /* Disable since we start from the same edge. */
1806 while (l_step) {
1807 if (!uvedit_face_visible_test(scene, l_step->f)) {
1808 break;
1809 }
1810
1811 if (use_face_select) {
1812 /* While selecting face loops is now done in a separate function #uv_select_faceloop(),
1813 * this check is still kept for edge ring selection, to keep it consistent with how edge
1814 * ring selection works in face mode in the 3D viewport. */
1815 uvedit_face_select_set_with_sticky(scene, bm, l_step->f, select, offsets);
1816 }
1817 else if (use_vertex_select) {
1818 uvedit_uv_select_set_with_sticky(scene, bm, l_step, select, offsets);
1819 uvedit_uv_select_set_with_sticky(scene, bm, l_step->next, select, offsets);
1820 }
1821 else {
1822 /* Edge select mode */
1823 uvedit_edge_select_set_with_sticky(scene, bm, l_step, select, offsets);
1824 }
1825
1827 if (l_step->f->len == 4) {
1828 BMLoop *l_step_opposite = l_step->next->next;
1830 scene, l_step_opposite, offsets);
1831 if (l_step == nullptr) {
1832 /* Ensure we touch the opposite edge if we can't walk over it. */
1833 l_step = l_step_opposite;
1834 }
1835 }
1836 else {
1837 l_step = nullptr;
1838 }
1839
1840 /* Break iteration when `l_step`:
1841 * - Is the first loop where we started from.
1842 * - Tagged using #BM_ELEM_TAG (meaning this loop has been visited in this iteration).
1843 * - Has its corresponding UV edge selected/unselected based on #select. */
1844 if (l_step && BM_elem_flag_test(l_step->e, BM_ELEM_TAG)) {
1845 /* Previously this check was not done and this resulted in the final edge in the edge ring
1846 * cycle to be skipped during selection (caused by old sticky selection behavior). */
1847 if (select && uvedit_edge_select_test(scene, l_step, offsets)) {
1848 break;
1849 }
1850 if (!select && !uvedit_edge_select_test(scene, l_step, offsets)) {
1851 break;
1852 }
1853 }
1854 }
1855 }
1856
1857 return (select) ? 1 : -1;
1858}
1859
1861
1862/* -------------------------------------------------------------------- */
1865
1867 const Span<Object *> objects,
1868 UvNearestHit *hit,
1869 const bool extend,
1870 bool deselect,
1871 const bool toggle,
1872 const bool select_faces)
1873{
1874 const bool uv_sync_select = (scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
1875
1876 /* loop over objects, or just use hit->ob */
1877 for (const int ob_index : objects.index_range()) {
1878 if (hit && ob_index != 0) {
1879 break;
1880 }
1881 Object *obedit = hit ? hit->ob : objects[ob_index];
1882
1883 BMFace *efa;
1884 BMLoop *l;
1885 BMIter iter, liter;
1886 UvMapVert *vlist, *iterv, *startv;
1887 int i, stacksize = 0, *stack;
1888 uint a;
1889 char *flag;
1890 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
1891
1892 if (uv_sync_select) {
1893 /* Pass. */
1894 }
1895 else {
1897 }
1898 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
1899
1900 BM_mesh_elem_table_ensure(bm, BM_FACE); /* we can use this too */
1901
1902 /* NOTE: we had 'use winding' so we don't consider overlapping islands as connected, see #44320
1903 * this made *every* projection split the island into front/back islands.
1904 * Keep 'use_winding' to false, see: #50970.
1905 *
1906 * Better solve this by having a delimit option for select-linked operator,
1907 * keeping island-select working as is. */
1908 UvVertMap *vmap = BM_uv_vert_map_create(bm, !uv_sync_select);
1909 if (vmap == nullptr) {
1910 continue;
1911 }
1912
1913 stack = MEM_malloc_arrayN<int>(bm->totface + 1, "UvLinkStack");
1914 flag = MEM_calloc_arrayN<char>(bm->totface, "UvLinkFlag");
1915
1916 if (hit == nullptr) {
1917 /* Use existing selection */
1918 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
1919 if (uvedit_face_visible_test(scene, efa)) {
1920 if (select_faces) {
1922 stack[stacksize] = a;
1923 stacksize++;
1924 flag[a] = 1;
1925 }
1926 }
1927 else {
1928 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1929 if (uvedit_uv_select_test(scene, l, offsets)) {
1930 bool add_to_stack = true;
1931 if (uv_sync_select) {
1932 /* Special case, vertex/edge & sync select being enabled.
1933 *
1934 * Without this, a second linked select will 'grow' each time as each new
1935 * selection reaches the boundaries of islands that share vertices but not UVs.
1936 *
1937 * Rules applied here:
1938 * - This loops face isn't selected.
1939 * - The only other fully selected face is connected or,
1940 * - There are no connected fully selected faces UV-connected to this loop.
1941 */
1942 BLI_assert(!select_faces);
1943 if (uvedit_face_select_test(scene, l->f, offsets)) {
1944 /* pass */
1945 }
1946 else {
1947 BMIter liter_other;
1948 BMLoop *l_other;
1949 BM_ITER_ELEM (l_other, &liter_other, l->v, BM_LOOPS_OF_VERT) {
1950 if ((l != l_other) && !BM_loop_uv_share_vert_check(l, l_other, offsets.uv) &&
1951 uvedit_face_select_test(scene, l_other->f, offsets))
1952 {
1953 add_to_stack = false;
1954 break;
1955 }
1956 }
1957 }
1958 }
1959
1960 if (add_to_stack) {
1961 stack[stacksize] = a;
1962 stacksize++;
1963 flag[a] = 1;
1964 break;
1965 }
1966 }
1967 }
1968 }
1969 }
1970 }
1971 }
1972 else {
1973 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
1974 if (efa == hit->efa) {
1975 stack[stacksize] = a;
1976 stacksize++;
1977 flag[a] = 1;
1978 break;
1979 }
1980 }
1981 }
1982
1983 while (stacksize > 0) {
1984
1985 stacksize--;
1986 a = stack[stacksize];
1987
1988 efa = BM_face_at_index(bm, a);
1989
1990 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1991
1992 /* make_uv_vert_map_EM sets verts tmp.l to the indices */
1993 vlist = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
1994
1995 startv = vlist;
1996
1997 for (iterv = vlist; iterv; iterv = iterv->next) {
1998 if (iterv->separate) {
1999 startv = iterv;
2000 }
2001 if (iterv->face_index == a) {
2002 break;
2003 }
2004 }
2005
2006 for (iterv = startv; iterv; iterv = iterv->next) {
2007 if ((startv != iterv) && (iterv->separate)) {
2008 break;
2009 }
2010 if (!flag[iterv->face_index]) {
2011 flag[iterv->face_index] = 1;
2012 stack[stacksize] = iterv->face_index;
2013 stacksize++;
2014 }
2015 }
2016 }
2017 }
2018
2019 /* Toggling - if any of the linked vertices is selected (and visible), we deselect. */
2020 if ((toggle == true) && (extend == false) && (deselect == false)) {
2021 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
2022 bool found_selected = false;
2023 if (!flag[a]) {
2024 continue;
2025 }
2026
2027 if (select_faces) {
2029 found_selected = true;
2030 }
2031 }
2032 else {
2033 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2034 if (uvedit_uv_select_test(scene, l, offsets)) {
2035 found_selected = true;
2036 break;
2037 }
2038 }
2039
2040 if (found_selected) {
2041 deselect = true;
2042 break;
2043 }
2044 }
2045 }
2046 }
2047
2048#define SET_SELECTION(value) \
2049 if (select_faces) { \
2050 BM_face_select_set(bm, efa, value); \
2051 } \
2052 else { \
2053 uvedit_face_select_set(scene, bm, efa, value, offsets); \
2054 } \
2055 (void)0
2056
2057 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) {
2058 if (!flag[a]) {
2059 if (!extend && !deselect && !toggle) {
2060 SET_SELECTION(false);
2061 }
2062 continue;
2063 }
2064
2065 if (!deselect) {
2066 SET_SELECTION(true);
2067 }
2068 else {
2069 SET_SELECTION(false);
2070 }
2071 }
2072
2073#undef SET_SELECTION
2074
2075 MEM_freeN(stack);
2076 MEM_freeN(flag);
2077 BM_uv_vert_map_free(vmap);
2078
2079 if (uv_sync_select) {
2080 if (deselect) {
2082 }
2083 else {
2084 if (!select_faces) {
2086 }
2087 }
2088 }
2089 }
2090}
2091
2093 BMVert *eve,
2094 const BMUVOffsets &offsets)
2095{
2096 BMIter liter;
2097 BMLoop *l;
2098
2099 BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
2100 if (!uvedit_face_visible_test(scene, l->f)) {
2101 continue;
2102 }
2103
2104 if (uvedit_uv_select_test(scene, l, offsets)) {
2105 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
2106 return luv;
2107 }
2108 }
2109
2110 return nullptr;
2111}
2112
2114
2115/* -------------------------------------------------------------------- */
2118
2120{
2121 Scene *scene = CTX_data_scene(C);
2122 ViewLayer *view_layer = CTX_data_view_layer(C);
2123
2124 BMFace *efa;
2125 BMLoop *l;
2126 BMIter iter, liter;
2127 const ToolSettings *ts = scene->toolsettings;
2128
2130 scene, view_layer, nullptr);
2131
2132 const bool is_uv_face_selectmode = (ts->uv_selectmode == UV_SELECT_FACE);
2133
2134 for (Object *obedit : objects) {
2135 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2136
2137 bool changed = false;
2138
2139 if (ts->uv_flag & UV_SYNC_SELECTION) {
2140 /* Pass. */
2141 }
2142 else {
2144 }
2145 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2146
2147 if (ts->uv_flag & UV_SYNC_SELECTION) {
2149 if (select) {
2150 EDBM_select_more(em, true);
2151 }
2152 else {
2153 EDBM_select_less(em, true);
2154 }
2155
2156 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2157 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2158 continue;
2159 }
2160
2161 if (is_uv_face_selectmode) {
2162
2163 /* clear tags */
2165
2166 /* mark loops to be selected */
2167 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2168 if (uvedit_face_visible_test(scene, efa)) {
2169
2170 if (select) {
2171#define NEIGHBORING_FACE_IS_SEL 1
2172#define CURR_FACE_IS_UNSEL 2
2173
2174 int sel_state = 0;
2175
2176 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2177 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert)) {
2178 sel_state |= NEIGHBORING_FACE_IS_SEL;
2179 }
2180 else {
2181 sel_state |= CURR_FACE_IS_UNSEL;
2182 }
2183
2184 if (!BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
2185 sel_state |= CURR_FACE_IS_UNSEL;
2186 }
2187
2188 /* If the current face is not selected and at least one neighboring face is
2189 * selected, then tag the current face to grow selection. */
2190 if (sel_state == (NEIGHBORING_FACE_IS_SEL | CURR_FACE_IS_UNSEL)) {
2192 changed = true;
2193 break;
2194 }
2195 }
2196
2197#undef NEIGHBORING_FACE_IS_SEL
2198#undef CURR_FACE_IS_UNSEL
2199 }
2200 else {
2201 if (!uvedit_face_select_test(scene, efa, offsets)) {
2202 continue;
2203 }
2204 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2205 /* Deselect face when at least one of the surrounding faces is not selected */
2206 if (!uvedit_vert_is_all_other_faces_selected(ts, l, offsets)) {
2208 changed = true;
2209 break;
2210 }
2211 }
2212 }
2213 }
2214 }
2215 }
2216 else {
2217
2218 /* clear tags */
2219 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2220 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2222 }
2223 }
2224
2225 /* mark loops to be selected */
2226 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2227 if (uvedit_face_visible_test(scene, efa)) {
2228 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2229
2230 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) == select) {
2233 changed = true;
2234 }
2235 }
2236 }
2237 }
2238 }
2239
2240 if (changed) {
2241 if (is_uv_face_selectmode) {
2242 /* Select tagged faces. */
2243 uv_select_flush_from_tag_face(scene, obedit, select);
2244 }
2245 else {
2246 /* Select tagged loops. */
2247 uv_select_flush_from_tag_loop(scene, obedit, select);
2248 /* Set/unset edge flags based on selected verts. */
2249 if (select) {
2250 uvedit_select_flush(scene, bm);
2251 }
2252 else {
2253 uvedit_deselect_flush(scene, bm);
2254 }
2255 }
2256 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2257 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2258 }
2259 }
2260
2261 return OPERATOR_FINISHED;
2262}
2263
2265{
2266 return uv_select_more_less(C, true);
2267}
2268
2270{
2271 /* identifiers */
2272 ot->name = "Select More";
2273 ot->description = "Select more UV vertices connected to initial selection";
2274 ot->idname = "UV_OT_select_more";
2275 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2276
2277 /* API callbacks. */
2278 ot->exec = uv_select_more_exec;
2280}
2281
2283{
2284 return uv_select_more_less(C, false);
2285}
2286
2288{
2289 /* identifiers */
2290 ot->name = "Select Less";
2291 ot->description = "Deselect UV vertices at the boundary of each selection region";
2292 ot->idname = "UV_OT_select_less";
2293 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2294
2295 /* API callbacks. */
2296 ot->exec = uv_select_less_exec;
2298}
2299
2301
2302/* -------------------------------------------------------------------- */
2305
2307{
2308 const ToolSettings *ts = scene->toolsettings;
2309 BMFace *efa;
2310 BMLoop *l;
2311 BMIter iter, liter;
2312
2313 if (ts->uv_flag & UV_SYNC_SELECTION) {
2314 return (bm->totvertsel || bm->totedgesel || bm->totfacesel);
2315 }
2316
2317 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2318
2319 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2320 if (!uvedit_face_visible_test(scene, efa)) {
2321 continue;
2322 }
2323 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2324 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert)) {
2325 return true;
2326 }
2327 }
2328 }
2329 return false;
2330}
2331
2333{
2334 bool found = false;
2335 for (Object *obedit : objects) {
2336 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2337 if (uvedit_select_is_any_selected(scene, bm)) {
2338 found = true;
2339 break;
2340 }
2341 }
2342 return found;
2343}
2344
2345static void uv_select_all(const Scene *scene, BMEditMesh *em, bool select_all)
2346{
2347 const ToolSettings *ts = scene->toolsettings;
2348 if (ts->uv_flag & UV_SYNC_SELECTION) {
2349 if (select_all) {
2351 }
2352 else {
2354 }
2355 return;
2356 }
2357
2358 BMFace *efa;
2359 BMLoop *l;
2360 BMIter iter, liter;
2361
2363 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
2364
2365 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2366 if (!uvedit_face_visible_test(scene, efa)) {
2367 continue;
2368 }
2369 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2370 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, select_all);
2371 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, select_all);
2372 }
2373 }
2374}
2375
2376static void uv_select_toggle_all(const Scene *scene, BMEditMesh *em)
2377{
2378 const ToolSettings *ts = scene->toolsettings;
2379 if (ts->uv_flag & UV_SYNC_SELECTION) {
2381 return;
2382 }
2383
2384 bool select_any = uvedit_select_is_any_selected(scene, em->bm);
2385 uv_select_all(scene, em, !select_any);
2386}
2387
2388static void uv_select_invert(const Scene *scene, BMEditMesh *em)
2389{
2390 const ToolSettings *ts = scene->toolsettings;
2391 if (ts->uv_flag & UV_SYNC_SELECTION) {
2392 EDBM_select_swap(em);
2394 return;
2395 }
2396
2397 BMesh *bm = em->bm;
2399 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2400 BMFace *efa;
2401 BMLoop *l;
2402 BMIter iter, liter;
2403 char uv_selectmode = ts->uv_selectmode;
2404 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2405 if (!uvedit_face_visible_test(scene, efa)) {
2406 continue;
2407 }
2408 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2409 if (ELEM(uv_selectmode, UV_SELECT_EDGE, UV_SELECT_FACE)) {
2410 /* Use UV edge selection to find vertices and edges that must be selected. */
2411 bool es = BM_ELEM_CD_GET_BOOL(l, offsets.select_edge);
2412 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, !es);
2413 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
2414 }
2415 /* Use UV vertex selection to find vertices and edges that must be selected. */
2416 else if (ELEM(uv_selectmode, UV_SELECT_VERTEX, UV_SELECT_ISLAND)) {
2417 bool vs = BM_ELEM_CD_GET_BOOL(l, offsets.select_vert);
2418 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, !vs);
2419 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
2420 }
2421 }
2422 }
2423
2424 /* Flush based on uv vert/edge flags and current UV select mode */
2425 if (ELEM(uv_selectmode, UV_SELECT_EDGE, UV_SELECT_FACE)) {
2427 }
2428 else if (ELEM(uv_selectmode, UV_SELECT_VERTEX, UV_SELECT_ISLAND)) {
2429 uvedit_select_flush(scene, bm);
2430 }
2431}
2432
2433static void uv_select_all_perform(const Scene *scene, Object *obedit, int action)
2434{
2436
2437 switch (action) {
2438 case SEL_TOGGLE: {
2439 uv_select_toggle_all(scene, em);
2440 break;
2441 }
2442 case SEL_SELECT: {
2443 uv_select_all(scene, em, true);
2444 break;
2445 }
2446 case SEL_DESELECT: {
2447 uv_select_all(scene, em, false);
2448 break;
2449 }
2450 case SEL_INVERT: {
2451 uv_select_invert(scene, em);
2452 break;
2453 }
2454 }
2455}
2456
2457static void uv_select_all_perform_multi_ex(const Scene *scene,
2458 const Span<Object *> objects,
2459 int action,
2460 const Object *ob_exclude)
2461{
2462 if (action == SEL_TOGGLE) {
2463 action = uvedit_select_is_any_selected_multi(scene, objects) ? SEL_DESELECT : SEL_SELECT;
2464 }
2465
2466 for (Object *obedit : objects) {
2467 if (ob_exclude && (obedit == ob_exclude)) {
2468 continue;
2469 }
2470 uv_select_all_perform(scene, obedit, action);
2471 }
2472}
2473
2474static void uv_select_all_perform_multi(const Scene *scene, Span<Object *> objects, int action)
2475{
2476 uv_select_all_perform_multi_ex(scene, objects, action, nullptr);
2477}
2478
2480{
2482 Scene *scene = CTX_data_scene(C);
2483 const ToolSettings *ts = scene->toolsettings;
2484 ViewLayer *view_layer = CTX_data_view_layer(C);
2485
2486 int action = RNA_enum_get(op->ptr, "action");
2487
2489 scene, view_layer, nullptr);
2490
2491 uv_select_all_perform_multi(scene, objects, action);
2492
2493 for (Object *obedit : objects) {
2495 }
2496
2497 return OPERATOR_FINISHED;
2498}
2499
2501{
2502 /* identifiers */
2503 ot->name = "(De)select All";
2504 ot->description = "Change selection of all UV vertices";
2505 ot->idname = "UV_OT_select_all";
2506 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2507
2508 /* API callbacks. */
2509 ot->exec = uv_select_all_exec;
2510 ot->poll = ED_operator_uvedit;
2511
2513}
2514
2516
2517/* -------------------------------------------------------------------- */
2520
2522 const Span<Object *> objects,
2523 const float co[2],
2525{
2527 const ARegion *region = CTX_wm_region(C);
2528 Scene *scene = CTX_data_scene(C);
2529 const ToolSettings *ts = scene->toolsettings;
2530 UvNearestHit hit = region ? uv_nearest_hit_init_dist_px(&region->v2d, 75.0f) :
2532 int selectmode, sticky;
2533 bool found_item = false;
2534 /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
2535 int flush = 0;
2536
2537 /* Penalty (in pixels) applied to elements that are already selected
2538 * so elements that aren't already selected are prioritized. */
2539 const float penalty_dist = 3.0f * U.pixelsize;
2540
2541 /* retrieve operation mode */
2542 if (ts->uv_flag & UV_SYNC_SELECTION) {
2543 if (ts->selectmode & SCE_SELECT_FACE) {
2544 selectmode = UV_SELECT_FACE;
2545 }
2546 else if (ts->selectmode & SCE_SELECT_EDGE) {
2547 selectmode = UV_SELECT_EDGE;
2548 }
2549 else {
2550 selectmode = UV_SELECT_VERTEX;
2551 }
2552
2553 sticky = SI_STICKY_DISABLE;
2554 }
2555 else {
2556 selectmode = ts->uv_selectmode;
2557 sticky = ts->uv_sticky;
2558 }
2559
2560 /* find nearest element */
2561 if (selectmode == UV_SELECT_VERTEX) {
2562 /* find vertex */
2563 found_item = uv_find_nearest_vert_multi(scene, objects, co, penalty_dist, &hit);
2564 if (found_item) {
2565 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
2568 }
2569 }
2570 }
2571 else if (selectmode == UV_SELECT_EDGE) {
2572 /* find edge */
2573 found_item = uv_find_nearest_edge_multi(scene, objects, co, penalty_dist, &hit);
2574 if (found_item) {
2575 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
2578 }
2579 }
2580 }
2581 else if (selectmode == UV_SELECT_FACE) {
2582 /* find face */
2583 found_item = uv_find_nearest_face_multi(scene, objects, co, &hit);
2584
2585 if (!found_item) {
2586 /* Fallback, perform a second pass without a limited threshold,
2587 * which succeeds as long as the cursor is inside the UV face.
2588 * Useful when zoomed in, to select faces with distant screen-space face centers. */
2589 hit.dist_sq = FLT_MAX;
2590 found_item = uv_find_nearest_face_multi_ex(scene, objects, co, &hit, true);
2591 }
2592
2593 if (found_item) {
2596 }
2597 }
2598 else if (selectmode == UV_SELECT_ISLAND) {
2599 found_item = uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit);
2600
2601 if (!found_item) {
2602 /* Without this, we can be within the face of an island but too far from an edge,
2603 * see face selection comment for details. */
2604 hit.dist_sq = FLT_MAX;
2605 found_item = uv_find_nearest_face_multi_ex(scene, objects, co, &hit, true);
2606 }
2607 }
2608
2609 bool found = found_item;
2610 bool changed = false;
2611
2612 bool is_selected = false;
2613 if (found) {
2614 Object *obedit = hit.ob;
2615 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2616 if (ts->uv_flag & UV_SYNC_SELECTION) {
2617 /* Pass. */
2618 }
2619 else {
2621 }
2622 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2623
2624 if (selectmode == UV_SELECT_FACE) {
2625 is_selected = uvedit_face_select_test(scene, hit.efa, offsets);
2626 }
2627 else if (selectmode == UV_SELECT_EDGE) {
2628 is_selected = uvedit_edge_select_test(scene, hit.l, offsets);
2629 }
2630 else {
2631 /* Vertex or island. For island (if we were using #uv_find_nearest_face_multi_ex, see above),
2632 * `hit.l` is null, use `hit.efa` instead. */
2633 if (hit.l != nullptr) {
2634 is_selected = uvedit_uv_select_test(scene, hit.l, offsets);
2635 }
2636 else {
2637 is_selected = uvedit_face_select_test(scene, hit.efa, offsets);
2638 }
2639 }
2640 }
2641
2642 if (params.sel_op == SEL_OP_SET) {
2643 if ((found && params.select_passthrough) && is_selected) {
2644 found = false;
2645 }
2646 else if (found || params.deselect_all) {
2647 /* Deselect everything. */
2649 for (Object *obedit : objects) {
2651 }
2652 changed = true;
2653 }
2654 }
2655
2656 if (found) {
2657 Object *obedit = hit.ob;
2658 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2659
2660 if (ts->uv_flag & UV_SYNC_SELECTION) {
2661 /* Pass. */
2662 }
2663 else {
2665 }
2666 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2667
2668 if (selectmode == UV_SELECT_ISLAND) {
2669 const bool extend = params.sel_op == SEL_OP_ADD;
2670 const bool deselect = params.sel_op == SEL_OP_SUB;
2671 const bool toggle = params.sel_op == SEL_OP_XOR;
2672 /* Current behavior of 'extend'
2673 * is actually toggling, so pass extend flag as 'toggle' here */
2674 uv_select_linked_multi(scene, objects, &hit, extend, deselect, toggle, false);
2675 /* TODO: check if this actually changed. */
2676 changed = true;
2677 }
2678 else {
2680 bool select_value = false;
2681 switch (params.sel_op) {
2682 case SEL_OP_ADD: {
2683 select_value = true;
2684 break;
2685 }
2686 case SEL_OP_SUB: {
2687 select_value = false;
2688 break;
2689 }
2690 case SEL_OP_XOR: {
2691 select_value = !is_selected;
2692 break;
2693 }
2694 case SEL_OP_SET: {
2695 /* Deselect has already been performed. */
2696 select_value = true;
2697 break;
2698 }
2699 case SEL_OP_AND: {
2700 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2701 break;
2702 }
2703 }
2704
2705 if (selectmode == UV_SELECT_FACE) {
2706 uvedit_face_select_set_with_sticky(scene, bm, hit.efa, select_value, offsets);
2707 flush = 1;
2708 }
2709 else if (selectmode == UV_SELECT_EDGE) {
2710 uvedit_edge_select_set_with_sticky(scene, bm, hit.l, select_value, offsets);
2711 flush = 1;
2712 }
2713 else if (selectmode == UV_SELECT_VERTEX) {
2714 uvedit_uv_select_set_with_sticky(scene, bm, hit.l, select_value, offsets);
2715 flush = 1;
2716 }
2717 else {
2719 }
2720
2721 /* De-selecting an edge may deselect a face too - validate. */
2722 if (ts->uv_flag & UV_SYNC_SELECTION) {
2723 if (select_value) {
2724 BMElem *ele = nullptr;
2725 if (selectmode == UV_SELECT_FACE) {
2726 ele = (BMElem *)hit.efa;
2727 }
2728 else if (selectmode == UV_SELECT_EDGE) {
2729 ele = (BMElem *)hit.l->e;
2730 }
2731 else if (selectmode == UV_SELECT_VERTEX) {
2732 ele = (BMElem *)hit.l->v;
2733 }
2734 /* Expected to be true, harmless if it's not. */
2735 if (ele && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
2737 }
2738 }
2739 else {
2741 }
2742 }
2743
2744 /* (de)select sticky UV nodes. */
2745 if (sticky != SI_STICKY_DISABLE) {
2746 flush = select_value ? 1 : -1;
2747 }
2748
2749 changed = true;
2750 }
2751
2752 if (ts->uv_flag & UV_SYNC_SELECTION) {
2753 if (flush != 0) {
2755 }
2756 }
2757 else {
2758 /* Setting the selection implies a single element, which doesn't need to be flushed. */
2759 if (params.sel_op != SEL_OP_SET) {
2761 }
2762 }
2763 }
2764
2765 if (changed && found) {
2766 /* Only update the `hit` object as de-selecting all will have refreshed the others. */
2767 Object *obedit = hit.ob;
2769 }
2770
2771 return changed || found;
2772}
2773static bool uv_mouse_select(bContext *C, const float co[2], const SelectPick_Params &params)
2774{
2775 const Scene *scene = CTX_data_scene(C);
2776 ViewLayer *view_layer = CTX_data_view_layer(C);
2778 scene, view_layer, nullptr);
2779 bool changed = uv_mouse_select_multi(C, objects, co, params);
2780 return changed;
2781}
2782
2784{
2785 float co[2];
2786
2787 RNA_float_get_array(op->ptr, "location", co);
2788
2790
2791 const bool changed = uv_mouse_select(C, co, params);
2792
2793 if (changed) {
2795 }
2797}
2798
2800{
2801 const ARegion *region = CTX_wm_region(C);
2802 float co[2];
2803
2804 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2805 RNA_float_set_array(op->ptr, "location", co);
2806
2807 const wmOperatorStatus retval = uv_select_exec(C, op);
2808
2809 return WM_operator_flag_only_pass_through_on_press(retval, event);
2810}
2811
2813{
2814 /* identifiers */
2815 ot->name = "Select";
2816 ot->description = "Select UV vertices";
2817 ot->idname = "UV_OT_select";
2818 ot->flag = OPTYPE_UNDO;
2819
2820 /* API callbacks. */
2821 ot->exec = uv_select_exec;
2822 ot->invoke = uv_select_invoke;
2823 ot->poll = ED_operator_uvedit; /* requires space image */
2824 ot->get_name = ED_select_pick_get_name;
2825
2826 /* properties */
2827 PropertyRNA *prop;
2828
2830
2831 prop = RNA_def_float_vector(
2832 ot->srna,
2833 "location",
2834 2,
2835 nullptr,
2836 -FLT_MAX,
2837 FLT_MAX,
2838 "Location",
2839 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2840 -100.0f,
2841 100.0f);
2843}
2844
2846
2847/* -------------------------------------------------------------------- */
2850
2855
2857 const Span<Object *> objects,
2858 const float co[2],
2859 const bool extend,
2860 enum eUVLoopGenericType loop_type)
2861{
2862 const ARegion *region = CTX_wm_region(C);
2864 Scene *scene = CTX_data_scene(C);
2865 const ToolSettings *ts = scene->toolsettings;
2866 UvNearestHit hit = region ? uv_nearest_hit_init_max(&region->v2d) :
2868 bool found_item = false;
2869 /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
2870 int flush = 0;
2871
2872 /* Find edge. */
2873 found_item = uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit);
2874 if (!found_item) {
2875 return OPERATOR_CANCELLED;
2876 }
2877
2878 Object *obedit = hit.ob;
2879 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2880
2881 /* Do selection. */
2882 if (!extend) {
2883 uv_select_all_perform_multi_ex(scene, objects, SEL_DESELECT, obedit);
2884 }
2885
2886 if (loop_type == UV_LOOP_SELECT) {
2888 flush = uv_select_faceloop(scene, obedit, &hit, extend);
2889 }
2890 else {
2891 flush = uv_select_edgeloop(scene, obedit, &hit, extend);
2892 }
2893 }
2894 else if (loop_type == UV_RING_SELECT) {
2895 flush = uv_select_edgering(scene, obedit, &hit, extend);
2896 }
2897 else {
2899 }
2900
2901 if (ts->uv_flag & UV_SYNC_SELECTION) {
2902 if (flush == 1) {
2904 }
2905 else if (flush == -1) {
2907 }
2908 }
2909 else {
2911 }
2912
2913 for (Object *ob : objects) {
2915 }
2916
2918}
2920 const float co[2],
2921 const bool extend,
2922 enum eUVLoopGenericType loop_type)
2923{
2924 const Scene *scene = CTX_data_scene(C);
2925 ViewLayer *view_layer = CTX_data_view_layer(C);
2927 scene, view_layer, nullptr);
2928 wmOperatorStatus ret = uv_mouse_select_loop_generic_multi(C, objects, co, extend, loop_type);
2929 return ret;
2930}
2931
2933
2934/* -------------------------------------------------------------------- */
2937
2939{
2940 float co[2];
2941
2942 RNA_float_get_array(op->ptr, "location", co);
2943 const bool extend = RNA_boolean_get(op->ptr, "extend");
2944
2945 return uv_mouse_select_loop_generic(C, co, extend, UV_LOOP_SELECT);
2946}
2947
2949{
2950 const ARegion *region = CTX_wm_region(C);
2951 float co[2];
2952
2953 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2954 RNA_float_set_array(op->ptr, "location", co);
2955
2956 const wmOperatorStatus retval = uv_select_loop_exec(C, op);
2957
2958 return WM_operator_flag_only_pass_through_on_press(retval, event);
2959}
2960
2962{
2963 /* identifiers */
2964 ot->name = "Loop Select";
2965 ot->description = "Select a loop of connected UV vertices";
2966 ot->idname = "UV_OT_select_loop";
2967 ot->flag = OPTYPE_UNDO;
2968
2969 /* API callbacks. */
2970 ot->exec = uv_select_loop_exec;
2971 ot->invoke = uv_select_loop_invoke;
2972 ot->poll = ED_operator_uvedit; /* requires space image */
2973
2974 /* properties */
2975 PropertyRNA *prop;
2976 prop = RNA_def_boolean(ot->srna,
2977 "extend",
2978 false,
2979 "Extend",
2980 "Extend selection rather than clearing the existing selection");
2982 prop = RNA_def_float_vector(
2983 ot->srna,
2984 "location",
2985 2,
2986 nullptr,
2987 -FLT_MAX,
2988 FLT_MAX,
2989 "Location",
2990 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2991 -100.0f,
2992 100.0f);
2994}
2995
2997
2998/* -------------------------------------------------------------------- */
3001
3003{
3004 float co[2];
3005 RNA_float_get_array(op->ptr, "location", co);
3006 const bool extend = RNA_boolean_get(op->ptr, "extend");
3007 return uv_mouse_select_loop_generic(C, co, extend, UV_RING_SELECT);
3008}
3009
3011 wmOperator *op,
3012 const wmEvent *event)
3013{
3014 const ARegion *region = CTX_wm_region(C);
3015 float co[2];
3016
3017 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
3018 RNA_float_set_array(op->ptr, "location", co);
3019
3020 const wmOperatorStatus retval = uv_select_edge_ring_exec(C, op);
3021
3022 return WM_operator_flag_only_pass_through_on_press(retval, event);
3023}
3024
3026{
3027 /* identifiers */
3028 ot->name = "Edge Ring Select";
3029 ot->description = "Select an edge ring of connected UV vertices";
3030 ot->idname = "UV_OT_select_edge_ring";
3031 ot->flag = OPTYPE_UNDO;
3032
3033 /* API callbacks. */
3036 ot->poll = ED_operator_uvedit; /* requires space image */
3037
3038 /* properties */
3039 PropertyRNA *prop;
3040 prop = RNA_def_boolean(ot->srna,
3041 "extend",
3042 false,
3043 "Extend",
3044 "Extend selection rather than clearing the existing selection");
3046 prop = RNA_def_float_vector(
3047 ot->srna,
3048 "location",
3049 2,
3050 nullptr,
3051 -FLT_MAX,
3052 FLT_MAX,
3053 "Location",
3054 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
3055 -100.0f,
3056 100.0f);
3058}
3059
3061
3062/* -------------------------------------------------------------------- */
3065
3067 wmOperator *op,
3068 const wmEvent *event,
3069 bool pick)
3070{
3071 const ARegion *region = CTX_wm_region(C);
3072 Scene *scene = CTX_data_scene(C);
3073 const ToolSettings *ts = scene->toolsettings;
3074 ViewLayer *view_layer = CTX_data_view_layer(C);
3075 bool extend = true;
3076 bool deselect = false;
3077 bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE);
3078
3079 UvNearestHit hit = region ? uv_nearest_hit_init_max(&region->v2d) :
3081
3082 if (pick) {
3083 extend = RNA_boolean_get(op->ptr, "extend");
3084 deselect = RNA_boolean_get(op->ptr, "deselect");
3085 }
3086
3088 scene, view_layer, nullptr);
3089
3090 if (pick) {
3091 float co[2];
3092
3093 if (event) {
3094 /* invoke */
3095 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
3096 RNA_float_set_array(op->ptr, "location", co);
3097 }
3098 else {
3099 /* exec */
3100 RNA_float_get_array(op->ptr, "location", co);
3101 }
3102
3103 if (!uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit)) {
3104 return OPERATOR_CANCELLED;
3105 }
3106 }
3107
3108 if (!extend && !deselect) {
3110 }
3111
3113 scene, objects, pick ? &hit : nullptr, extend, deselect, false, select_faces);
3114
3115 if (pick) {
3118 }
3119 else {
3120 for (Object *obedit : objects) {
3121 DEG_id_tag_update(static_cast<ID *>(obedit->data),
3123 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3124 }
3125 }
3126
3127 return OPERATOR_FINISHED;
3128}
3129
3131{
3132 return uv_select_linked_internal(C, op, nullptr, false);
3133}
3134
3136{
3137 /* identifiers */
3138 ot->name = "Select Linked";
3139 ot->description = "Select all UV vertices linked to the active UV map";
3140 ot->idname = "UV_OT_select_linked";
3141
3142 /* API callbacks. */
3143 ot->exec = uv_select_linked_exec;
3144 ot->poll = ED_operator_uvedit; /* requires space image */
3145
3146 /* flags */
3147 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3148}
3149
3151
3152/* -------------------------------------------------------------------- */
3155
3157 wmOperator *op,
3158 const wmEvent *event)
3159{
3160 return uv_select_linked_internal(C, op, event, true);
3161}
3162
3164{
3165 return uv_select_linked_internal(C, op, nullptr, true);
3166}
3167
3169{
3170 /* identifiers */
3171 ot->name = "Select Linked Pick";
3172 ot->description = "Select all UV vertices linked under the mouse";
3173 ot->idname = "UV_OT_select_linked_pick";
3174
3175 /* flags */
3176 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3177
3178 /* API callbacks. */
3181 ot->poll = ED_operator_uvedit; /* requires space image */
3182
3183 /* properties */
3184 PropertyRNA *prop;
3185 prop = RNA_def_boolean(ot->srna,
3186 "extend",
3187 false,
3188 "Extend",
3189 "Extend selection rather than clearing the existing selection");
3191 prop = RNA_def_boolean(ot->srna,
3192 "deselect",
3193 false,
3194 "Deselect",
3195 "Deselect linked UV vertices rather than selecting them");
3197 prop = RNA_def_float_vector(
3198 ot->srna,
3199 "location",
3200 2,
3201 nullptr,
3202 -FLT_MAX,
3203 FLT_MAX,
3204 "Location",
3205 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
3206 -100.0f,
3207 100.0f);
3209}
3210
3212
3213/* -------------------------------------------------------------------- */
3216
3224{
3226 Scene *scene = CTX_data_scene(C);
3227 ViewLayer *view_layer = CTX_data_view_layer(C);
3228 const ToolSettings *ts = scene->toolsettings;
3229
3230 BMFace *efa;
3231 BMLoop *l;
3232 BMIter iter, liter;
3233
3234 if (ts->uv_flag & UV_SYNC_SELECTION) {
3235 BKE_report(op->reports, RPT_ERROR, "Cannot split selection when sync selection is enabled");
3236 return OPERATOR_CANCELLED;
3237 }
3238
3239 bool changed_multi = false;
3240
3242 scene, view_layer, nullptr);
3243
3244 for (Object *obedit : objects) {
3245 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3246
3247 bool changed = false;
3248
3250 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3251
3252 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3253 bool is_sel = false;
3254 bool is_unsel = false;
3255
3256 if (!uvedit_face_visible_test(scene, efa)) {
3257 continue;
3258 }
3259
3260 /* are we all selected? */
3261 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3262
3263 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) ||
3265 {
3266 is_sel = true;
3267 }
3268 if (!BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) ||
3270 {
3271 is_unsel = true;
3272 }
3273
3274 /* we have mixed selection, bail out */
3275 if (is_sel && is_unsel) {
3276 break;
3277 }
3278 }
3279
3280 if (is_sel && is_unsel) {
3281 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3282 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
3283 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
3284 }
3285
3286 changed = true;
3287 }
3288 }
3289
3290 if (changed) {
3291 changed_multi = true;
3294 }
3295 }
3296
3297 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3298}
3299
3301{
3302 /* identifiers */
3303 ot->name = "Select Split";
3304 ot->description = "Select only entirely selected faces";
3305 ot->idname = "UV_OT_select_split";
3306 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3307
3308 /* API callbacks. */
3309 ot->exec = uv_select_split_exec;
3310 ot->poll = ED_operator_uvedit; /* requires space image */
3311}
3312
3314 const ToolSettings *ts,
3315 Object *obedit)
3316{
3317 if (ts->uv_flag & UV_SYNC_SELECTION) {
3318 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3320 }
3321 else {
3322 Object *obedit_eval = DEG_get_evaluated(depsgraph, obedit);
3323 BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(obedit_eval->data),
3325 /* Only for region redraw. */
3327 }
3328}
3329
3331
3332/* -------------------------------------------------------------------- */
3337
3342 const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
3343{
3344 uvedit_uv_select_set(scene, bm, l, select, offsets);
3345
3346 BMVert *v = l->v;
3347 BLI_assert(v->e);
3348 const BMEdge *e_iter, *e_first;
3349 e_iter = e_first = v->e;
3350 do {
3351 if (e_iter->l == nullptr) {
3352 continue;
3353 }
3354 BMLoop *l_first = e_iter->l;
3355 BMLoop *l_iter = l_first;
3356 do {
3357 if (!(l_iter->v == v && l_iter != l)) {
3358 continue;
3359 }
3360 if (!uvedit_face_visible_test(scene, l_iter->f)) {
3361 continue;
3362 }
3363 if (BM_loop_uv_share_vert_check(l, l_iter, offsets.uv)) {
3364 uvedit_uv_select_set(scene, bm, l_iter, select, offsets);
3365 }
3366 } while ((l_iter = l_iter->radial_next) != l_first);
3367 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
3368}
3369
3380static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select)
3381{
3382 /* Selecting UV Faces with some modes requires us to change
3383 * the selection in other faces (depending on the sticky mode).
3384 *
3385 * This only needs to be done when the Mesh is not used for
3386 * selection (so for sticky modes, vertex or location based). */
3387
3388 const ToolSettings *ts = scene->toolsettings;
3389 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3390 BMFace *efa;
3391 BMLoop *l;
3392 BMIter iter, liter;
3393
3394 if (ts->uv_flag & UV_SYNC_SELECTION) {
3395 /* Pass. */
3396 }
3397 else {
3399 }
3400 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3401
3402 bool use_sticky = true;
3403 if (ts->uv_flag & UV_SYNC_SELECTION) {
3404 /* Use the mesh selection directly. */
3405 use_sticky = false;
3406 }
3407 if (ts->uv_sticky == SI_STICKY_DISABLE) {
3408 /* No need for sticky calculation when it's disabled. */
3409 use_sticky = false;
3410 }
3411
3412 if (use_sticky) {
3413 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3414 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
3415 continue;
3416 }
3417
3418 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3419 if (select) {
3420 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
3422 }
3423 else {
3424 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
3425 if (!uvedit_vert_is_face_select_any_other(ts, l, offsets)) {
3427 }
3428 }
3429 }
3430 }
3431 }
3432 else {
3433 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3434 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
3435 continue;
3436 }
3437 uvedit_face_select_set(scene, bm, efa, select, offsets);
3438 }
3439 }
3440}
3441
3452static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select)
3453{
3454 /* Selecting UV Loops with some modes requires us to change
3455 * the selection in other faces (depending on the sticky mode).
3456 *
3457 * This only needs to be done when the Mesh is not used for
3458 * selection (so for sticky modes, vertex or location based). */
3459
3460 const ToolSettings *ts = scene->toolsettings;
3461 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3462 BMFace *efa;
3463 BMLoop *l;
3464 BMIter iter, liter;
3465
3466 if (ts->uv_flag & UV_SYNC_SELECTION) {
3467 /* Pass. */
3468 }
3469 else {
3471 }
3472 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3473
3474 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && ts->uv_sticky == SI_STICKY_VERTEX) {
3475 /* Tag all verts as untouched, then touch the ones that have a face center
3476 * in the loop and select all UVs that use a touched vert. */
3478
3479 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3480 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3483 }
3484 }
3485 }
3486
3487 /* now select tagged verts */
3488 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3489 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3490 if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
3491 uvedit_uv_select_set(scene, bm, l, select, offsets);
3492 }
3493 }
3494 }
3495 }
3496 else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && ts->uv_sticky == SI_STICKY_LOC) {
3497 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3498 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3501 }
3502 }
3503 }
3504 }
3505 else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
3506 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3507 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3509 uvedit_uv_select_set(scene, bm, l, select, offsets);
3510 }
3511 }
3512 }
3513 }
3514}
3515
3527{
3528 const ToolSettings *ts = scene->toolsettings;
3529 BMFace *efa;
3530 BMLoop *l;
3531 BMIter iter, liter;
3532
3533 if (ts->uv_flag & UV_SYNC_SELECTION) {
3534 /* Pass. */
3535 }
3536 else {
3538 }
3539 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3540
3541 bool use_sticky = true;
3542 if (ts->uv_flag & UV_SYNC_SELECTION) {
3543 /* Use the mesh selection directly. */
3544 use_sticky = false;
3545 }
3546 if (ts->uv_sticky == SI_STICKY_DISABLE) {
3547 /* No need for sticky calculation when it's disabled. */
3548 use_sticky = false;
3549 }
3550
3551 if (use_sticky) {
3552 /* Use UV edge selection to identify which verts must to be selected */
3553
3554 /* Clear UV vert flags */
3555 bm_clear_uv_vert_selection(scene, bm, offsets);
3556
3557 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3558 if (!uvedit_face_visible_test(scene, efa)) {
3559 /* This visibility check could be removed? Simply relying on edge flags to ensure
3560 * visibility might be sufficient. */
3561 continue;
3562 }
3563 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3564 /* Select verts based on UV edge flag. */
3565 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
3566 uv_select_flush_from_tag_sticky_loc_internal(scene, bm, l, true, offsets);
3567 uv_select_flush_from_tag_sticky_loc_internal(scene, bm, l->next, true, offsets);
3568 }
3569 }
3570 }
3571 }
3572 else {
3573 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3574 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3575
3576 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
3577 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
3578 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, true);
3579 }
3580 else if (!BM_ELEM_CD_GET_BOOL(l->prev, offsets.select_edge)) {
3581 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, false);
3582 }
3583 }
3584 }
3585 }
3586}
3587
3589
3590/* -------------------------------------------------------------------- */
3593
3595{
3597 Scene *scene = CTX_data_scene(C);
3598 const ToolSettings *ts = scene->toolsettings;
3599 ViewLayer *view_layer = CTX_data_view_layer(C);
3600 const ARegion *region = CTX_wm_region(C);
3601 BMFace *efa;
3602 BMLoop *l;
3603 BMIter iter, liter;
3604 float *luv;
3605 rctf rectf;
3606 const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3607 (ts->selectmode == SCE_SELECT_FACE) :
3608 (ts->uv_selectmode == UV_SELECT_FACE));
3609 const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3610 (ts->selectmode == SCE_SELECT_EDGE) :
3611 (ts->uv_selectmode == UV_SELECT_EDGE));
3612 const bool use_select_linked = !(ts->uv_flag & UV_SYNC_SELECTION) &&
3614
3615 /* get rectangle from operator */
3617 UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
3618
3619 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
3620 const bool select = (sel_op != SEL_OP_SUB);
3621 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
3622
3623 const bool pinned = RNA_boolean_get(op->ptr, "pinned");
3624
3625 bool changed_multi = false;
3626
3628 scene, view_layer, nullptr);
3629
3630 if (use_pre_deselect) {
3632 }
3633
3634 /* don't indent to avoid diff noise! */
3635 for (Object *obedit : objects) {
3636 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3637
3638 bool changed = false;
3639
3640 if (ts->uv_flag & UV_SYNC_SELECTION) {
3641 /* Pass. */
3642
3643 /* NOTE: sync selection can't do pinned. */
3644 }
3645 else {
3647 if (pinned) {
3648 const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2);
3649 BM_uv_map_attr_pin_ensure(bm, active_uv_name);
3650 }
3651 }
3652 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3653
3654 /* do actual selection */
3655 if (use_face_center && !pinned) {
3656 /* handle face selection mode */
3657 float cent[2];
3658
3659 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3660 /* assume not touched */
3662
3663 if (uvedit_face_visible_test(scene, efa)) {
3664 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
3665 if (BLI_rctf_isect_pt_v(&rectf, cent)) {
3667 changed = true;
3668 }
3669 }
3670 }
3671
3672 /* (de)selects all tagged faces and deals with sticky modes */
3673 if (changed) {
3674 uv_select_flush_from_tag_face(scene, obedit, select);
3675 }
3676 }
3677 else if (use_edge && !pinned) {
3678 bool do_second_pass = true;
3679 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3680 if (!uvedit_face_visible_test(scene, efa)) {
3681 continue;
3682 }
3683
3684 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
3685 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
3686
3687 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3688 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3689 if (BLI_rctf_isect_pt_v(&rectf, luv) && BLI_rctf_isect_pt_v(&rectf, luv_prev)) {
3690 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
3691 changed = true;
3692 do_second_pass = false;
3693 }
3694 l_prev = l;
3695 luv_prev = luv;
3696 }
3697 }
3698 /* Do a second pass if no complete edges could be selected.
3699 * This matches wire-frame edit-mesh selection in the 3D view. */
3700 if (do_second_pass) {
3701 /* Second pass to check if edges partially overlap with the selection area (box). */
3702 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3703 if (!uvedit_face_visible_test(scene, efa)) {
3704 continue;
3705 }
3706 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
3707 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
3708
3709 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3710 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3711 if (BLI_rctf_isect_segment(&rectf, luv_prev, luv)) {
3712 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
3713 changed = true;
3714 }
3715 l_prev = l;
3716 luv_prev = luv;
3717 }
3718 }
3719 }
3720 }
3721 else {
3722 /* other selection modes */
3723 changed = true;
3725
3726 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3727 if (!uvedit_face_visible_test(scene, efa)) {
3728 continue;
3729 }
3730 bool has_selected = false;
3731 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3732 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3733 if (select != uvedit_uv_select_test(scene, l, offsets)) {
3734 if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION)) {
3735 /* UV_SYNC_SELECTION - can't do pinned selection */
3736 if (BLI_rctf_isect_pt_v(&rectf, luv)) {
3737 uvedit_uv_select_set(scene, bm, l, select, offsets);
3739 has_selected = true;
3740 }
3741 }
3742 else if (pinned) {
3743 if (BM_ELEM_CD_GET_BOOL(l, offsets.pin) && BLI_rctf_isect_pt_v(&rectf, luv)) {
3744 uvedit_uv_select_set(scene, bm, l, select, offsets);
3746 }
3747 }
3748 }
3749 }
3750 if (has_selected && use_select_linked) {
3751 UvNearestHit hit = {};
3752 hit.ob = obedit;
3753 hit.efa = efa;
3754 uv_select_linked_multi(scene, objects, &hit, true, !select, false, false);
3755 }
3756 }
3757
3758 if (ts->uv_sticky == SI_STICKY_VERTEX) {
3759 uvedit_vertex_select_tagged(bm, scene, select, offsets);
3760 }
3761 }
3762
3763 if (changed || use_pre_deselect) {
3764 changed_multi = true;
3765 if (ts->uv_flag & UV_SYNC_SELECTION) {
3767 }
3768 else {
3770 }
3772 }
3773 }
3774
3775 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3776}
3777
3779{
3780 /* identifiers */
3781 ot->name = "Box Select";
3782 ot->description = "Select UV vertices using box selection";
3783 ot->idname = "UV_OT_select_box";
3784
3785 /* API callbacks. */
3786 ot->invoke = WM_gesture_box_invoke;
3787 ot->exec = uv_box_select_exec;
3788 ot->modal = WM_gesture_box_modal;
3789 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
3790 ot->cancel = WM_gesture_box_cancel;
3791
3792 /* flags */
3793 ot->flag = OPTYPE_UNDO;
3794
3795 /* properties */
3796 RNA_def_boolean(ot->srna, "pinned", false, "Pinned", "Border select pinned UVs only");
3797
3800}
3801
3803
3804/* -------------------------------------------------------------------- */
3807
3808static bool uv_circle_select_is_point_inside(const float uv[2],
3809 const float offset[2],
3810 const float ellipse[2])
3811{
3812 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
3813 const float co[2] = {
3814 (uv[0] - offset[0]) * ellipse[0],
3815 (uv[1] - offset[1]) * ellipse[1],
3816 };
3817 return len_squared_v2(co) < 1.0f;
3818}
3819
3820static bool uv_circle_select_is_edge_inside(const float uv_a[2],
3821 const float uv_b[2],
3822 const float offset[2],
3823 const float ellipse[2])
3824{
3825 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
3826 const float co_a[2] = {
3827 (uv_a[0] - offset[0]) * ellipse[0],
3828 (uv_a[1] - offset[1]) * ellipse[1],
3829 };
3830 const float co_b[2] = {
3831 (uv_b[0] - offset[0]) * ellipse[0],
3832 (uv_b[1] - offset[1]) * ellipse[1],
3833 };
3834 const float co_zero[2] = {0.0f, 0.0f};
3835 return dist_squared_to_line_segment_v2(co_zero, co_a, co_b) < 1.0f;
3836}
3837
3839{
3842 Scene *scene = CTX_data_scene(C);
3843 ViewLayer *view_layer = CTX_data_view_layer(C);
3844 const ToolSettings *ts = scene->toolsettings;
3845 const ARegion *region = CTX_wm_region(C);
3846 BMFace *efa;
3847 BMLoop *l;
3848 BMIter iter, liter;
3849 float *luv;
3850 int x, y, radius, width, height;
3851 float zoomx, zoomy;
3852 float offset[2], ellipse[2];
3853
3854 const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3855 (ts->selectmode == SCE_SELECT_FACE) :
3856 (ts->uv_selectmode == UV_SELECT_FACE));
3857 const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3858 (ts->selectmode == SCE_SELECT_EDGE) :
3859 (ts->uv_selectmode == UV_SELECT_EDGE));
3860 const bool use_select_linked = !(ts->uv_flag & UV_SYNC_SELECTION) &&
3862
3863 /* get operator properties */
3864 x = RNA_int_get(op->ptr, "x");
3865 y = RNA_int_get(op->ptr, "y");
3866 radius = RNA_int_get(op->ptr, "radius");
3867
3868 /* compute ellipse size and location, not a circle since we deal
3869 * with non square image. ellipse is normalized, r = 1.0. */
3870 ED_space_image_get_size(sima, &width, &height);
3871 ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
3872
3873 ellipse[0] = width * zoomx / radius;
3874 ellipse[1] = height * zoomy / radius;
3875
3876 UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
3877
3878 bool changed_multi = false;
3879
3881 scene, view_layer, nullptr);
3882
3883 const eSelectOp sel_op = ED_select_op_modal(
3884 eSelectOp(RNA_enum_get(op->ptr, "mode")),
3885 WM_gesture_is_modal_first(static_cast<wmGesture *>(op->customdata)));
3886 const bool select = (sel_op != SEL_OP_SUB);
3887 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
3888
3889 if (use_pre_deselect) {
3891 }
3892
3893 for (Object *obedit : objects) {
3894 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3895
3896 bool changed = false;
3897
3898 if (ts->uv_flag & UV_SYNC_SELECTION) {
3899 /* Pass. */
3900 }
3901 else {
3903 }
3904 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
3905
3906 /* do selection */
3907 if (use_face_center) {
3908 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3910 /* assume not touched */
3911 if (select != uvedit_face_select_test(scene, efa, offsets)) {
3912 float cent[2];
3913 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
3914 if (uv_circle_select_is_point_inside(cent, offset, ellipse)) {
3916 changed = true;
3917 }
3918 }
3919 }
3920
3921 /* (de)selects all tagged faces and deals with sticky modes */
3922 if (changed) {
3923 uv_select_flush_from_tag_face(scene, obedit, select);
3924 }
3925 }
3926 else if (use_edge) {
3927 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3928 if (!uvedit_face_visible_test(scene, efa)) {
3929 continue;
3930 }
3931
3932 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
3933 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
3934
3935 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3936 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3937 if (uv_circle_select_is_edge_inside(luv, luv_prev, offset, ellipse)) {
3938 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
3939 changed = true;
3940 }
3941 l_prev = l;
3942 luv_prev = luv;
3943 }
3944 }
3945 }
3946 else {
3948
3949 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3950 if (!uvedit_face_visible_test(scene, efa)) {
3951 continue;
3952 }
3953 bool has_selected = false;
3954 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3955 if (select != uvedit_uv_select_test(scene, l, offsets)) {
3956 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3957 if (uv_circle_select_is_point_inside(luv, offset, ellipse)) {
3958 changed = true;
3959 uvedit_uv_select_set(scene, bm, l, select, offsets);
3961 has_selected = true;
3962 }
3963 }
3964 }
3965 if (has_selected && use_select_linked) {
3966 UvNearestHit hit = {};
3967 hit.ob = obedit;
3968 hit.efa = efa;
3969 uv_select_linked_multi(scene, objects, &hit, true, !select, false, false);
3970 }
3971 }
3972
3973 if (ts->uv_sticky == SI_STICKY_VERTEX) {
3974 uvedit_vertex_select_tagged(bm, scene, select, offsets);
3975 }
3976 }
3977
3978 if (changed || use_pre_deselect) {
3979 changed_multi = true;
3980 if (ts->uv_flag & UV_SYNC_SELECTION) {
3982 }
3983 else {
3985 }
3987 }
3988 }
3989
3990 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3991}
3992
3994{
3995 /* identifiers */
3996 ot->name = "Circle Select";
3997 ot->description = "Select UV vertices using circle selection";
3998 ot->idname = "UV_OT_select_circle";
3999
4000 /* API callbacks. */
4001 ot->invoke = WM_gesture_circle_invoke;
4002 ot->modal = WM_gesture_circle_modal;
4003 ot->exec = uv_circle_select_exec;
4004 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
4005 ot->cancel = WM_gesture_circle_cancel;
4006 ot->get_name = ED_select_circle_get_name;
4007
4008 /* flags */
4009 ot->flag = OPTYPE_UNDO;
4010
4011 /* properties */
4014}
4015
4017
4018/* -------------------------------------------------------------------- */
4021
4023 const rcti *clip_rect,
4024 const Span<int2> mcoords,
4025 const float co_test[2])
4026{
4027 int co_screen[2];
4029 &region->v2d, co_test[0], co_test[1], &co_screen[0], &co_screen[1]) &&
4030 BLI_rcti_isect_pt_v(clip_rect, co_screen) &&
4031 BLI_lasso_is_point_inside(mcoords, co_screen[0], co_screen[1], V2D_IS_CLIPPED))
4032 {
4033 return true;
4034 }
4035 return false;
4036}
4037
4039 const rcti *clip_rect,
4040 const Span<int2> mcoords,
4041 const float co_test_a[2],
4042 const float co_test_b[2])
4043{
4044 int co_screen_a[2], co_screen_b[2];
4046 &region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) &&
4047 BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) &&
4049 mcoords, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED))
4050 {
4051 return true;
4052 }
4053 return false;
4054}
4055
4056static bool do_lasso_select_mesh_uv(bContext *C, const Span<int2> mcoords, const eSelectOp sel_op)
4057{
4059 const ARegion *region = CTX_wm_region(C);
4060 Scene *scene = CTX_data_scene(C);
4061 const ToolSettings *ts = scene->toolsettings;
4062 ViewLayer *view_layer = CTX_data_view_layer(C);
4063 const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
4064 (ts->selectmode == SCE_SELECT_FACE) :
4065 (ts->uv_selectmode == UV_SELECT_FACE));
4066 const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
4067 (ts->selectmode == SCE_SELECT_EDGE) :
4068 (ts->uv_selectmode == UV_SELECT_EDGE));
4069 const bool use_select_linked = !(ts->uv_flag & UV_SYNC_SELECTION) &&
4071
4072 const bool select = (sel_op != SEL_OP_SUB);
4073 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
4074
4075 BMIter iter, liter;
4076
4077 BMFace *efa;
4078 BMLoop *l;
4079 bool changed_multi = false;
4080 rcti rect;
4081
4082 BLI_lasso_boundbox(&rect, mcoords);
4083
4085 scene, view_layer, nullptr);
4086
4087 if (use_pre_deselect) {
4089 }
4090
4091 for (Object *obedit : objects) {
4092
4093 bool changed = false;
4094
4095 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4096
4097 if (ts->uv_flag & UV_SYNC_SELECTION) {
4098 /* Pass. */
4099 }
4100 else {
4102 }
4103 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4104
4105 if (use_face_center) { /* Face Center Select. */
4106 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4108 /* assume not touched */
4109 if (select != uvedit_face_select_test(scene, efa, offsets)) {
4110 float cent[2];
4111 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
4112 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, cent)) {
4114 changed = true;
4115 }
4116 }
4117 }
4118
4119 /* (de)selects all tagged faces and deals with sticky modes */
4120 if (changed) {
4121 uv_select_flush_from_tag_face(scene, obedit, select);
4122 }
4123 }
4124 else if (use_edge) {
4125 bool do_second_pass = true;
4126 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4127 if (!uvedit_face_visible_test(scene, efa)) {
4128 continue;
4129 }
4130
4131 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
4132 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
4133
4134 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4135 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4136 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv) &&
4137 do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv_prev))
4138 {
4139 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
4140 do_second_pass = false;
4141 changed = true;
4142 }
4143 l_prev = l;
4144 luv_prev = luv;
4145 }
4146 }
4147 /* Do a second pass if no complete edges could be selected.
4148 * This matches wire-frame edit-mesh selection in the 3D view. */
4149 if (do_second_pass) {
4150 /* Second pass to check if edges partially overlap with the selection area (lasso). */
4151 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4152 if (!uvedit_face_visible_test(scene, efa)) {
4153 continue;
4154 }
4155 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
4156 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
4157
4158 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4159 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4160 if (do_lasso_select_mesh_uv_is_edge_inside(region, &rect, mcoords, luv, luv_prev)) {
4161 uvedit_edge_select_set_with_sticky(scene, bm, l_prev, select, offsets);
4162 changed = true;
4163 }
4164 l_prev = l;
4165 luv_prev = luv;
4166 }
4167 }
4168 }
4169 }
4170 else { /* Vert Selection. */
4172
4173 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4174 if (!uvedit_face_visible_test(scene, efa)) {
4175 continue;
4176 }
4177 bool has_selected = false;
4178 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4179 if (select != uvedit_uv_select_test(scene, l, offsets)) {
4180 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4181 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv)) {
4182 uvedit_uv_select_set(scene, bm, l, select, offsets);
4183 changed = true;
4185 has_selected = true;
4186 }
4187 }
4188 }
4189 if (has_selected && use_select_linked) {
4190 UvNearestHit hit = {};
4191 hit.ob = obedit;
4192 hit.efa = efa;
4193 uv_select_linked_multi(scene, objects, &hit, true, !select, false, false);
4194 }
4195 }
4196
4197 if (ts->uv_sticky == SI_STICKY_VERTEX) {
4198 uvedit_vertex_select_tagged(bm, scene, select, offsets);
4199 }
4200 }
4201
4202 if (changed || use_pre_deselect) {
4203 changed_multi = true;
4204 if (ts->uv_flag & UV_SYNC_SELECTION) {
4206 }
4207 else {
4209 }
4211 }
4212 }
4213
4214 return changed_multi;
4215}
4216
4218{
4220 if (mcoords.is_empty()) {
4221 return OPERATOR_PASS_THROUGH;
4222 }
4223
4224 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
4225 bool changed = do_lasso_select_mesh_uv(C, mcoords, sel_op);
4226
4227 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
4228}
4229
4231{
4232 ot->name = "Lasso Select UV";
4233 ot->description = "Select UVs using lasso selection";
4234 ot->idname = "UV_OT_select_lasso";
4235
4236 ot->invoke = WM_gesture_lasso_invoke;
4237 ot->modal = WM_gesture_lasso_modal;
4238 ot->exec = uv_lasso_select_exec;
4240 ot->cancel = WM_gesture_lasso_cancel;
4241
4242 /* flags */
4244
4245 /* properties */
4248}
4249
4251
4252/* -------------------------------------------------------------------- */
4255
4257{
4258 Scene *scene = CTX_data_scene(C);
4259 const ToolSettings *ts = scene->toolsettings;
4260
4261 /* Use this operator only in vertex mode, since it is not guaranteed that pinned vertices may
4262 * form higher selection states (like edges/faces/islands) in other modes. */
4263 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
4264 if (ts->uv_selectmode != UV_SELECT_VERTEX) {
4265 BKE_report(op->reports, RPT_ERROR, "Pinned vertices can be selected in Vertex Mode only");
4266 return OPERATOR_CANCELLED;
4267 }
4268 }
4269
4271 ViewLayer *view_layer = CTX_data_view_layer(C);
4272 BMFace *efa;
4273 BMLoop *l;
4274 BMIter iter, liter;
4275
4277 scene, view_layer, nullptr);
4278
4279 for (Object *obedit : objects) {
4280 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4281
4282 const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2);
4283 if (!BM_uv_map_attr_pin_exists(bm, active_uv_name)) {
4284 continue;
4285 }
4286
4287 bool changed = false;
4288 if (ts->uv_flag & UV_SYNC_SELECTION) {
4289 /* Pass. */
4290 }
4291 else {
4293 }
4294 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4295
4296 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4297 if (!uvedit_face_visible_test(scene, efa)) {
4298 continue;
4299 }
4300
4301 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4302
4303 if (BM_ELEM_CD_GET_BOOL(l, offsets.pin)) {
4304 uvedit_uv_select_enable(scene, bm, l, offsets);
4305 changed = true;
4306 }
4307 }
4308 }
4309 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
4311 }
4312
4313 if (changed) {
4315 }
4316 }
4317
4318 return OPERATOR_FINISHED;
4319}
4320
4322{
4323 /* identifiers */
4324 ot->name = "Selected Pinned";
4325 ot->description = "Select all pinned UV vertices";
4326 ot->idname = "UV_OT_select_pinned";
4327 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4328
4329 /* API callbacks. */
4330 ot->exec = uv_select_pinned_exec;
4331 ot->poll = ED_operator_uvedit;
4332}
4333
4335
4336/* -------------------------------------------------------------------- */
4339
4340BLI_INLINE uint overlap_hash(const void *overlap_v)
4341{
4342 const BVHTreeOverlap *overlap = static_cast<const BVHTreeOverlap *>(overlap_v);
4343
4344 /* Designed to treat (A,B) and (B,A) as the same. */
4345 int x = overlap->indexA;
4346 int y = overlap->indexB;
4347 if (x > y) {
4348 std::swap(x, y);
4349 }
4350 return BLI_hash_int_2d(x, y);
4351}
4352
4353BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v)
4354{
4355 const BVHTreeOverlap *a = static_cast<const BVHTreeOverlap *>(a_v);
4356 const BVHTreeOverlap *b = static_cast<const BVHTreeOverlap *>(b_v);
4357 return !((a->indexA == b->indexA && a->indexB == b->indexB) ||
4358 (a->indexA == b->indexB && a->indexB == b->indexA));
4359}
4360
4364 float tri[3][2];
4365};
4366
4374static bool overlap_tri_tri_uv_test(const float t1[3][2],
4375 const float t2[3][2],
4376 const float endpoint_bias)
4377{
4378 float vi[2];
4379
4380 /* Don't use 'isect_tri_tri_v2' here
4381 * because it's important to ignore overlap at end-points. */
4382 if (isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[0], t2[1], endpoint_bias, vi) == 1 ||
4383 isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[1], t2[2], endpoint_bias, vi) == 1 ||
4384 isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[2], t2[0], endpoint_bias, vi) == 1 ||
4385 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[0], t2[1], endpoint_bias, vi) == 1 ||
4386 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[1], t2[2], endpoint_bias, vi) == 1 ||
4387 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[2], t2[0], endpoint_bias, vi) == 1 ||
4388 isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[0], t2[1], endpoint_bias, vi) == 1 ||
4389 isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[1], t2[2], endpoint_bias, vi) == 1)
4390 {
4391 return true;
4392 }
4393
4394 /* When none of the segments intersect, checking if either of the triangles corners
4395 * is inside the others is almost always sufficient to test if the two triangles intersect.
4396 *
4397 * However, the `endpoint_bias` on segment intersections causes _exact_ overlapping
4398 * triangles not to be detected.
4399 *
4400 * Resolve this problem at the small cost of calculating the triangle center, see #85508. */
4401 mid_v2_v2v2v2(vi, UNPACK3(t1));
4402 if (isect_point_tri_v2(vi, UNPACK3(t2)) != 0) {
4403 return true;
4404 }
4405 mid_v2_v2v2v2(vi, UNPACK3(t2));
4406 if (isect_point_tri_v2(vi, UNPACK3(t1)) != 0) {
4407 return true;
4408 }
4409
4410 return false;
4411}
4412
4414{
4416 Scene *scene = CTX_data_scene(C);
4417 ViewLayer *view_layer = CTX_data_view_layer(C);
4418
4420 scene, view_layer, nullptr);
4421
4422 /* Calculate maximum number of tree nodes and prepare initial selection. */
4423 uint uv_tri_len = 0;
4424 for (Object *obedit : objects) {
4425 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4426
4430 if (!extend) {
4431 uv_select_all_perform(scene, obedit, SEL_DESELECT);
4432 }
4433
4434 BMIter iter;
4435 BMFace *efa;
4436 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4437 if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) {
4438 continue;
4439 }
4440 uv_tri_len += efa->len - 2;
4441 }
4442 }
4443
4444 UVOverlapData *overlap_data = MEM_malloc_arrayN<UVOverlapData>(uv_tri_len, "UvOverlapData");
4445 BVHTree *uv_tree = BLI_bvhtree_new(uv_tri_len, 0.0f, 4, 6);
4446
4447 /* Use a global data index when inserting into the BVH. */
4448 int data_index = 0;
4449
4450 int face_len_alloc = 3;
4451 float(*uv_verts)[2] = static_cast<float(*)[2]>(
4452 MEM_mallocN(sizeof(*uv_verts) * face_len_alloc, "UvOverlapCoords"));
4453 uint(*indices)[3] = static_cast<uint(*)[3]>(
4454 MEM_mallocN(sizeof(*indices) * (face_len_alloc - 2), "UvOverlapTris"));
4455
4458
4459 for (const int ob_index : objects.index_range()) {
4460 Object *obedit = objects[ob_index];
4461 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
4462 BMIter iter, liter;
4463 BMFace *efa;
4464 BMLoop *l;
4465
4466 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
4467
4468 /* Triangulate each UV face and store it inside the BVH. */
4469 int face_index;
4470 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, face_index) {
4471
4472 if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) {
4473 continue;
4474 }
4475
4476 const uint face_len = efa->len;
4477 const uint tri_len = face_len - 2;
4478
4479 if (face_len_alloc < face_len) {
4480 MEM_freeN(uv_verts);
4482 uv_verts = static_cast<float(*)[2]>(
4483 MEM_mallocN(sizeof(*uv_verts) * face_len, "UvOverlapCoords"));
4484 indices = static_cast<uint(*)[3]>(
4485 MEM_mallocN(sizeof(*indices) * tri_len, "UvOverlapTris"));
4486 face_len_alloc = face_len;
4487 }
4488
4489 int vert_index;
4490 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, vert_index) {
4491 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
4492 copy_v2_v2(uv_verts[vert_index], luv);
4493 }
4494
4495 /* The UV coordinates winding could be positive of negative,
4496 * determine it automatically. */
4497 const int coords_sign = 0;
4498 BLI_polyfill_calc_arena(uv_verts, face_len, coords_sign, indices, arena);
4499
4500 /* A beauty fill is necessary to remove degenerate triangles that may be produced from the
4501 * above poly-fill (see #103913), otherwise the overlap tests can fail. */
4502 BLI_polyfill_beautify(uv_verts, face_len, indices, arena, heap);
4503
4504 for (int t = 0; t < tri_len; t++) {
4505 overlap_data[data_index].ob_index = ob_index;
4506 overlap_data[data_index].face_index = face_index;
4507
4508 /* BVH needs 3D, overlap data uses 2D. */
4509 const float tri[3][3] = {
4510 {UNPACK2(uv_verts[indices[t][0]]), 0.0f},
4511 {UNPACK2(uv_verts[indices[t][1]]), 0.0f},
4512 {UNPACK2(uv_verts[indices[t][2]]), 0.0f},
4513 };
4514
4515 copy_v2_v2(overlap_data[data_index].tri[0], tri[0]);
4516 copy_v2_v2(overlap_data[data_index].tri[1], tri[1]);
4517 copy_v2_v2(overlap_data[data_index].tri[2], tri[2]);
4518
4519 BLI_bvhtree_insert(uv_tree, data_index, &tri[0][0], 3);
4520 data_index++;
4521 }
4522
4523 BLI_memarena_clear(arena);
4524 BLI_heap_clear(heap, nullptr);
4525 }
4526 }
4527 BLI_assert(data_index == uv_tri_len);
4528
4529 BLI_memarena_free(arena);
4530 BLI_heap_free(heap, nullptr);
4531 MEM_freeN(uv_verts);
4533
4534 BLI_bvhtree_balance(uv_tree);
4535
4536 uint tree_overlap_len;
4537 BVHTreeOverlap *overlap = BLI_bvhtree_overlap_self(uv_tree, &tree_overlap_len, nullptr, nullptr);
4538
4539 if (overlap != nullptr) {
4540 GSet *overlap_set = BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, tree_overlap_len);
4541
4542 for (int i = 0; i < tree_overlap_len; i++) {
4543 /* Skip overlaps against yourself. */
4544 if (overlap[i].indexA == overlap[i].indexB) {
4545 continue;
4546 }
4547
4548 /* Skip overlaps that have already been tested. */
4549 if (!BLI_gset_add(overlap_set, &overlap[i])) {
4550 continue;
4551 }
4552
4553 const UVOverlapData *o_a = &overlap_data[overlap[i].indexA];
4554 const UVOverlapData *o_b = &overlap_data[overlap[i].indexB];
4555 Object *obedit_a = objects[o_a->ob_index];
4556 Object *obedit_b = objects[o_b->ob_index];
4557 BMesh *bm_a = BKE_editmesh_from_object(obedit_a)->bm;
4558 BMesh *bm_b = BKE_editmesh_from_object(obedit_b)->bm;
4559 BMFace *face_a = bm_a->ftable[o_a->face_index];
4560 BMFace *face_b = bm_b->ftable[o_b->face_index];
4561
4562 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
4563 /* Pass. */
4564 }
4565 else {
4566 const char *uv_a_name = CustomData_get_active_layer_name(&bm_a->ldata, CD_PROP_FLOAT2);
4567 const char *uv_b_name = CustomData_get_active_layer_name(&bm_b->ldata, CD_PROP_FLOAT2);
4568 BM_uv_map_attr_vert_select_ensure(bm_a, uv_a_name);
4569 BM_uv_map_attr_vert_select_ensure(bm_b, uv_b_name);
4570 BM_uv_map_attr_edge_select_ensure(bm_a, uv_a_name);
4571 BM_uv_map_attr_edge_select_ensure(bm_b, uv_b_name);
4572 }
4573 const BMUVOffsets offsets_a = BM_uv_map_offsets_get(bm_a);
4574 const BMUVOffsets offsets_b = BM_uv_map_offsets_get(bm_b);
4575
4576 /* Skip if both faces are already selected. */
4577 if (uvedit_face_select_test(scene, face_a, offsets_a) &&
4578 uvedit_face_select_test(scene, face_b, offsets_b))
4579 {
4580 continue;
4581 }
4582
4583 /* Main tri-tri overlap test. */
4584 const float endpoint_bias = -1e-4f;
4585 if (overlap_tri_tri_uv_test(o_a->tri, o_b->tri, endpoint_bias)) {
4586 uvedit_face_select_enable(scene, bm_a, face_a, offsets_a);
4587 uvedit_face_select_enable(scene, bm_b, face_b, offsets_b);
4588 }
4589 }
4590
4591 BLI_gset_free(overlap_set, nullptr);
4592 MEM_freeN(overlap);
4593 }
4594
4595 for (Object *object : objects) {
4597 }
4598
4599 BLI_bvhtree_free(uv_tree);
4600
4601 MEM_freeN(overlap_data);
4602
4603 return OPERATOR_FINISHED;
4604}
4605
4607{
4608 bool extend = RNA_boolean_get(op->ptr, "extend");
4609 return uv_select_overlap(C, extend);
4610}
4611
4613{
4614 /* identifiers */
4615 ot->name = "Select Overlap";
4616 ot->description = "Select all UV faces which overlap each other";
4617 ot->idname = "UV_OT_select_overlap";
4618 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4619
4620 /* API callbacks. */
4621 ot->exec = uv_select_overlap_exec;
4622 ot->poll = ED_operator_uvedit;
4623
4624 /* properties */
4625 RNA_def_boolean(ot->srna,
4626 "extend",
4627 false,
4628 "Extend",
4629 "Extend selection rather than clearing the existing selection");
4630}
4631
4635
4636static float get_uv_vert_needle(const eUVSelectSimilar type,
4637 BMVert *vert,
4638 const float ob_m3[3][3],
4639 BMLoop *loop,
4640 const BMUVOffsets &offsets)
4641{
4642 BLI_assert(offsets.pin >= 0);
4643 BLI_assert(offsets.uv >= 0);
4644
4645 float result = 0.0f;
4646 switch (type) {
4647 case UV_SSIM_AREA_UV: {
4648 BMFace *f;
4649 BMIter iter;
4650 BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) {
4651 result += BM_face_calc_area_uv(f, offsets.uv);
4652 }
4653 break;
4654 }
4655 case UV_SSIM_AREA_3D: {
4656 BMFace *f;
4657 BMIter iter;
4658 BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) {
4660 }
4661 break;
4662 }
4663 case UV_SSIM_SIDES: {
4664 BMEdge *e;
4665 BMIter iter;
4666 BM_ITER_ELEM (e, &iter, vert, BM_EDGES_OF_VERT) {
4667 result += 1.0f;
4668 }
4669 break;
4670 }
4671 case UV_SSIM_PIN:
4672 return BM_ELEM_CD_GET_BOOL(loop, offsets.pin) ? 1.0f : 0.0f;
4673 default:
4675 return false;
4676 }
4677
4678 return result;
4679}
4680
4681static float get_uv_edge_needle(const eUVSelectSimilar type,
4682 BMEdge *edge,
4683 const float ob_m3[3][3],
4684 BMLoop *loop_a,
4685 BMLoop *loop_b,
4686 const BMUVOffsets &offsets)
4687{
4688 BLI_assert(offsets.pin >= 0);
4689 BLI_assert(offsets.uv >= 0);
4690 float result = 0.0f;
4691 switch (type) {
4692 case UV_SSIM_AREA_UV: {
4693 BMFace *f;
4694 BMIter iter;
4695 BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) {
4696 result += BM_face_calc_area_uv(f, offsets.uv);
4697 }
4698 break;
4699 }
4700 case UV_SSIM_AREA_3D: {
4701 BMFace *f;
4702 BMIter iter;
4703 BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) {
4705 }
4706 break;
4707 }
4708 case UV_SSIM_LENGTH_UV: {
4709 float *luv_a = BM_ELEM_CD_GET_FLOAT_P(loop_a, offsets.uv);
4710 float *luv_b = BM_ELEM_CD_GET_FLOAT_P(loop_b, offsets.uv);
4711 return len_v2v2(luv_a, luv_b);
4712 }
4713 case UV_SSIM_LENGTH_3D:
4714 return len_v3v3(edge->v1->co, edge->v2->co);
4715 case UV_SSIM_SIDES: {
4716 BMEdge *e;
4717 BMIter iter;
4718 BM_ITER_ELEM (e, &iter, edge, BM_FACES_OF_EDGE) {
4719 result += 1.0f;
4720 }
4721 break;
4722 }
4723 case UV_SSIM_PIN: {
4724 if (BM_ELEM_CD_GET_BOOL(loop_a, offsets.pin)) {
4725 result += 1.0f;
4726 }
4727 if (BM_ELEM_CD_GET_BOOL(loop_b, offsets.pin)) {
4728 result += 1.0f;
4729 }
4730 break;
4731 }
4732 default:
4734 return false;
4735 }
4736
4737 return result;
4738}
4739
4740static float get_uv_face_needle(const eUVSelectSimilar type,
4741 BMFace *face,
4742 int ob_index,
4743 const float ob_m3[3][3],
4744 const BMUVOffsets &offsets)
4745{
4746 BLI_assert(offsets.pin >= 0);
4747 BLI_assert(offsets.uv >= 0);
4748 float result = 0.0f;
4749 switch (type) {
4750 case UV_SSIM_AREA_UV:
4751 return BM_face_calc_area_uv(face, offsets.uv);
4752 case UV_SSIM_AREA_3D:
4753 return BM_face_calc_area_with_mat3(face, ob_m3);
4754 case UV_SSIM_SIDES:
4755 return face->len;
4756 case UV_SSIM_OBJECT:
4757 return ob_index;
4758 case UV_SSIM_PIN: {
4759 BMLoop *l;
4760 BMIter liter;
4761 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4762 if (BM_ELEM_CD_GET_BOOL(l, offsets.pin)) {
4763 result += 1.0f;
4764 }
4765 }
4766 break;
4767 }
4768 case UV_SSIM_MATERIAL:
4769 return face->mat_nr;
4770 case UV_SSIM_WINDING:
4771 return signum_i(BM_face_calc_area_uv_signed(face, offsets.uv));
4772 default:
4774 return false;
4775 }
4776 return result;
4777}
4778
4780 const FaceIsland *island,
4781 const float ob_m3[3][3],
4782 const BMUVOffsets &offsets)
4783
4784{
4785 BLI_assert(offsets.uv >= 0);
4786 float result = 0.0f;
4787 switch (type) {
4788 case UV_SSIM_AREA_UV:
4789 for (int i = 0; i < island->faces_len; i++) {
4790 result += BM_face_calc_area_uv(island->faces[i], offsets.uv);
4791 }
4792 break;
4793 case UV_SSIM_AREA_3D:
4794 for (int i = 0; i < island->faces_len; i++) {
4795 result += BM_face_calc_area_with_mat3(island->faces[i], ob_m3);
4796 }
4797 break;
4798 case UV_SSIM_FACE:
4799 return island->faces_len;
4800 default:
4802 return false;
4803 }
4804 return result;
4805}
4806
4808{
4809 Scene *scene = CTX_data_scene(C);
4810 ViewLayer *view_layer = CTX_data_view_layer(C);
4813
4814 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
4815 const float threshold = RNA_float_get(op->ptr, "threshold");
4816 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
4817
4819 scene, view_layer, nullptr);
4820
4821 int max_verts_selected_all = 0;
4822 for (Object *ob : objects) {
4824 BMFace *face;
4825 BMIter iter;
4826 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
4827 if (!uvedit_face_visible_test(scene, face)) {
4828 continue;
4829 }
4830 max_verts_selected_all += face->len;
4831 }
4832 /* TODO: Get a tighter bounds */
4833 }
4834
4835 int tree_index = 0;
4836 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_verts_selected_all);
4837
4838 for (Object *ob : objects) {
4840 if (bm->totvertsel == 0) {
4841 continue;
4842 }
4843
4844 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4845 float ob_m3[3][3];
4846 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4847
4848 BMFace *face;
4849 BMIter iter;
4850 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
4851 if (!uvedit_face_visible_test(scene, face)) {
4852 continue;
4853 }
4854 BMLoop *l;
4855 BMIter liter;
4856 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4857 if (!uvedit_uv_select_test(scene, l, offsets)) {
4858 continue;
4859 }
4860 float needle = get_uv_vert_needle(type, l->v, ob_m3, l, offsets);
4861 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
4862 }
4863 }
4864 }
4865
4866 if (tree_1d != nullptr) {
4867 BLI_kdtree_1d_deduplicate(tree_1d);
4868 BLI_kdtree_1d_balance(tree_1d);
4869 }
4870
4871 for (Object *ob : objects) {
4873 if (bm->totvertsel == 0) {
4874 /* No selection means no visible UV's unless sync-select is enabled. */
4875 if (!(ts->uv_flag & UV_SYNC_SELECTION)) {
4876 continue;
4877 }
4878 }
4879
4880 bool changed = false;
4881
4882 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4883 float ob_m3[3][3];
4884 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4885
4886 BMFace *face;
4887 BMIter iter;
4888 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
4889 if (!uvedit_face_visible_test(scene, face)) {
4890 continue;
4891 }
4892 BMLoop *l;
4893 BMIter liter;
4894 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4895 if (uvedit_uv_select_test(scene, l, offsets)) {
4896 continue; /* Already selected. */
4897 }
4898 const float needle = get_uv_vert_needle(type, l->v, ob_m3, l, offsets);
4899 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
4900 if (select) {
4901 uvedit_uv_select_set(scene, bm, l, select, offsets);
4902 changed = true;
4903 }
4904 }
4905 }
4906 if (changed) {
4907 if (ts->uv_flag & UV_SYNC_SELECTION) {
4909 }
4910 else {
4911 uvedit_select_flush(scene, bm);
4912 }
4914 }
4915 }
4916
4917 BLI_kdtree_1d_free(tree_1d);
4918 return OPERATOR_FINISHED;
4919}
4920
4922{
4923 Scene *scene = CTX_data_scene(C);
4924 ViewLayer *view_layer = CTX_data_view_layer(C);
4927
4928 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
4929 const float threshold = RNA_float_get(op->ptr, "threshold");
4930 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
4931
4933 scene, view_layer, nullptr);
4934
4935 int max_edges_selected_all = 0;
4936 for (Object *ob : objects) {
4938 BMFace *face;
4939 BMIter iter;
4940 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
4941 if (!uvedit_face_visible_test(scene, face)) {
4942 continue;
4943 }
4944 max_edges_selected_all += face->len;
4945 }
4946 /* TODO: Get a tighter bounds. */
4947 }
4948
4949 int tree_index = 0;
4950 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_edges_selected_all);
4951
4952 for (Object *ob : objects) {
4954 if (bm->totvertsel == 0) {
4955 continue;
4956 }
4957
4958 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4959 float ob_m3[3][3];
4960 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4961
4962 BMFace *face;
4963 BMIter iter;
4964 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
4965 if (!uvedit_face_visible_test(scene, face)) {
4966 continue;
4967 }
4968 BMLoop *l;
4969 BMIter liter;
4970 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4971 if (!uvedit_edge_select_test(scene, l, offsets)) {
4972 continue;
4973 }
4974
4975 float needle = get_uv_edge_needle(type, l->e, ob_m3, l, l->next, offsets);
4976 if (tree_1d) {
4977 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
4978 }
4979 }
4980 }
4981 }
4982
4983 if (tree_1d != nullptr) {
4984 BLI_kdtree_1d_deduplicate(tree_1d);
4985 BLI_kdtree_1d_balance(tree_1d);
4986 }
4987
4988 for (Object *ob : objects) {
4990 if (bm->totvertsel == 0) {
4991 /* No selection means no visible UV's unless sync-select is enabled. */
4992 if (!(ts->uv_flag & UV_SYNC_SELECTION)) {
4993 continue;
4994 }
4995 }
4996
4997 bool changed = false;
4998 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
4999 float ob_m3[3][3];
5000 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
5001
5002 BMFace *face;
5003 BMIter iter;
5004 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
5005 if (!uvedit_face_visible_test(scene, face)) {
5006 continue;
5007 }
5008 BMLoop *l;
5009 BMIter liter;
5010 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
5011 if (uvedit_edge_select_test(scene, l, offsets)) {
5012 continue; /* Already selected. */
5013 }
5014
5015 float needle = get_uv_edge_needle(type, l->e, ob_m3, l, l->next, offsets);
5016 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
5017 if (select) {
5018 uvedit_edge_select_set(scene, bm, l, select, offsets);
5019 changed = true;
5020 }
5021 }
5022 }
5023 if (changed) {
5024 if (ts->uv_flag & UV_SYNC_SELECTION) {
5026 }
5027 else {
5028 uvedit_select_flush(scene, bm);
5029 }
5031 }
5032 }
5033
5034 BLI_kdtree_1d_free(tree_1d);
5035 return OPERATOR_FINISHED;
5036}
5037
5039{
5040 Scene *scene = CTX_data_scene(C);
5041 ViewLayer *view_layer = CTX_data_view_layer(C);
5044
5045 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
5046 const float threshold = RNA_float_get(op->ptr, "threshold");
5047 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
5048
5050 scene, view_layer, nullptr);
5051
5052 int max_faces_selected_all = 0;
5053 for (Object *ob : objects) {
5055 max_faces_selected_all += bm->totfacesel;
5056 /* TODO: Get a tighter bounds */
5057 }
5058
5059 int tree_index = 0;
5060 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_faces_selected_all);
5061
5062 for (const int ob_index : objects.index_range()) {
5063 Object *ob = objects[ob_index];
5065 if (bm->totvertsel == 0) {
5066 continue;
5067 }
5068
5069 float ob_m3[3][3];
5070 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
5071
5072 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5073
5074 BMFace *face;
5075 BMIter iter;
5076 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
5077 if (!uvedit_face_visible_test(scene, face)) {
5078 continue;
5079 }
5080 if (!uvedit_face_select_test(scene, face, offsets)) {
5081 continue;
5082 }
5083
5084 float needle = get_uv_face_needle(type, face, ob_index, ob_m3, offsets);
5085 if (tree_1d) {
5086 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
5087 }
5088 }
5089 }
5090
5091 if (tree_1d != nullptr) {
5092 BLI_kdtree_1d_deduplicate(tree_1d);
5093 BLI_kdtree_1d_balance(tree_1d);
5094 }
5095
5096 for (const int ob_index : objects.index_range()) {
5097 Object *ob = objects[ob_index];
5099 if (bm->totvertsel == 0) {
5100 /* No selection means no visible UV's unless sync-select is enabled. */
5101 if (!(ts->uv_flag & UV_SYNC_SELECTION)) {
5102 continue;
5103 }
5104 }
5105
5106 bool changed = false;
5107 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5108
5109 float ob_m3[3][3];
5110 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
5111
5112 BMFace *face;
5113 BMIter iter;
5114 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
5115 if (!uvedit_face_visible_test(scene, face)) {
5116 continue;
5117 }
5118 if (uvedit_face_select_test(scene, face, offsets)) {
5119 continue;
5120 }
5121
5122 float needle = get_uv_face_needle(type, face, ob_index, ob_m3, offsets);
5123
5124 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
5125 if (select) {
5126 uvedit_face_select_set(scene, bm, face, select, offsets);
5127 changed = true;
5128 }
5129 }
5130 if (changed) {
5131 if (ts->uv_flag & UV_SYNC_SELECTION) {
5133 }
5134 else {
5135 uvedit_select_flush(scene, bm);
5136 }
5138 }
5139 }
5140
5141 BLI_kdtree_1d_free(tree_1d);
5142 return OPERATOR_FINISHED;
5143}
5144
5145static bool uv_island_selected(const Scene *scene, FaceIsland *island)
5146{
5147 BLI_assert(island && island->faces_len);
5148 return uvedit_face_select_test(scene, island->faces[0], island->offsets);
5149}
5150
5152{
5153 Scene *scene = CTX_data_scene(C);
5154 ViewLayer *view_layer = CTX_data_view_layer(C);
5157
5158 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
5159 const float threshold = RNA_float_get(op->ptr, "threshold");
5160 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
5161
5163 scene, view_layer, nullptr);
5164
5165 ListBase *island_list_ptr = MEM_calloc_arrayN<ListBase>(objects.size(), __func__);
5166 int island_list_len = 0;
5167
5168 const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
5169
5170 for (const int ob_index : objects.index_range()) {
5171 Object *obedit = objects[ob_index];
5172 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
5173 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5174 float aspect_y = 1.0f; /* Placeholder value, aspect doesn't change connectivity. */
5175 island_list_len += bm_mesh_calc_uv_islands(
5176 scene, bm, &island_list_ptr[ob_index], face_selected, false, false, aspect_y, offsets);
5177 }
5178
5179 FaceIsland **island_array = static_cast<FaceIsland **>(
5180 MEM_callocN(sizeof(*island_array) * island_list_len, __func__));
5181
5182 int tree_index = 0;
5183 KDTree_1d *tree_1d = BLI_kdtree_1d_new(island_list_len);
5184
5185 for (const int ob_index : objects.index_range()) {
5186 Object *obedit = objects[ob_index];
5187 float ob_m3[3][3];
5188 copy_m3_m4(ob_m3, obedit->object_to_world().ptr());
5189
5190 int index;
5191 LISTBASE_FOREACH_INDEX (FaceIsland *, island, &island_list_ptr[ob_index], index) {
5192 island_array[index] = island;
5193 if (!uv_island_selected(scene, island)) {
5194 continue;
5195 }
5196 float needle = get_uv_island_needle(type, island, ob_m3, island->offsets);
5197 if (tree_1d) {
5198 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
5199 }
5200 }
5201 }
5202
5203 if (tree_1d != nullptr) {
5204 BLI_kdtree_1d_deduplicate(tree_1d);
5205 BLI_kdtree_1d_balance(tree_1d);
5206 }
5207
5208 int tot_island_index = 0;
5209 for (const int ob_index : objects.index_range()) {
5210 Object *obedit = objects[ob_index];
5211 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
5212 float ob_m3[3][3];
5213 copy_m3_m4(ob_m3, obedit->object_to_world().ptr());
5214
5215 bool changed = false;
5216 int index;
5217 LISTBASE_FOREACH_INDEX (FaceIsland *, island, &island_list_ptr[ob_index], index) {
5218 island_array[tot_island_index++] = island; /* To deallocate later. */
5219 if (uv_island_selected(scene, island)) {
5220 continue;
5221 }
5222 float needle = get_uv_island_needle(type, island, ob_m3, island->offsets);
5223 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
5224 if (!select) {
5225 continue;
5226 }
5227 for (int j = 0; j < island->faces_len; j++) {
5228 uvedit_face_select_set(scene, bm, island->faces[j], select, island->offsets);
5229 }
5230 changed = true;
5231 }
5232
5233 if (changed) {
5234 if (ts->uv_flag & UV_SYNC_SELECTION) {
5236 }
5237 else {
5238 uvedit_select_flush(scene, bm);
5239 }
5241 }
5242 }
5243
5244 BLI_assert(tot_island_index == island_list_len);
5245 for (int i = 0; i < island_list_len; i++) {
5246 MEM_SAFE_FREE(island_array[i]->faces);
5247 MEM_SAFE_FREE(island_array[i]);
5248 }
5249
5250 MEM_SAFE_FREE(island_array);
5251 MEM_SAFE_FREE(island_list_ptr);
5252 BLI_kdtree_1d_free(tree_1d);
5253
5254 return OPERATOR_FINISHED;
5255}
5256
5257/* Select similar UV faces/edges/verts based on current selection. */
5259{
5261 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
5262
5263 if (!RNA_property_is_set(op->ptr, prop)) {
5265 }
5266 else {
5267 ts->select_thresh = RNA_property_float_get(op->ptr, prop);
5268 }
5269
5270 int selectmode = (ts->uv_flag & UV_SYNC_SELECTION) ? ts->selectmode : ts->uv_selectmode;
5271 if (selectmode & UV_SELECT_EDGE) {
5272 return uv_select_similar_edge_exec(C, op);
5273 }
5274 if (selectmode & UV_SELECT_FACE) {
5275 return uv_select_similar_face_exec(C, op);
5276 }
5277 if (selectmode & UV_SELECT_ISLAND) {
5278 return uv_select_similar_island_exec(C, op);
5279 }
5280
5281 return uv_select_similar_vert_exec(C, op);
5282}
5283
5285 {UV_SSIM_PIN, "PIN", 0, "Pinned", ""},
5286 {UV_SSIM_LENGTH_UV, "LENGTH", 0, "Length", ""},
5287 {UV_SSIM_LENGTH_3D, "LENGTH_3D", 0, "Length 3D", ""},
5288 {UV_SSIM_AREA_UV, "AREA", 0, "Area", ""},
5289 {UV_SSIM_AREA_3D, "AREA_3D", 0, "Area 3D", ""},
5290 {UV_SSIM_MATERIAL, "MATERIAL", 0, "Material", ""},
5291 {UV_SSIM_OBJECT, "OBJECT", 0, "Object", ""},
5292 {UV_SSIM_SIDES, "SIDES", 0, "Polygon Sides", ""},
5293 {UV_SSIM_WINDING, "WINDING", 0, "Winding", ""},
5294 {UV_SSIM_FACE, "FACE", 0, "Amount of Faces in Island", ""},
5295 {0}};
5296
5297static EnumPropertyItem prop_similar_compare_types[] = {{SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
5298 {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
5299 {SIM_CMP_LT, "LESS", 0, "Less", ""},
5300 {0}};
5301
5303 PointerRNA * /*ptr*/,
5304 PropertyRNA * /*prop*/,
5305 bool *r_free)
5306{
5307 EnumPropertyItem *item = nullptr;
5308 int totitem = 0;
5309
5311 if (ts) {
5312 int selectmode = (ts->uv_flag & UV_SYNC_SELECTION) ? ts->selectmode : ts->uv_selectmode;
5313 if (selectmode & UV_SELECT_VERTEX) {
5315 }
5316 else if (selectmode & UV_SELECT_EDGE) {
5320 }
5321 else if (selectmode & UV_SELECT_FACE) {
5328 }
5329 else if (selectmode & UV_SELECT_ISLAND) {
5333 }
5334 }
5335 else {
5337 }
5338
5339 RNA_enum_item_end(&item, &totitem);
5340 *r_free = true;
5341 return item;
5342}
5343
5345{
5346 /* identifiers */
5347 ot->name = "Select Similar";
5348 ot->description = "Select similar UVs by property types";
5349 ot->idname = "UV_OT_select_similar";
5350
5351 /* API callbacks. */
5352 ot->invoke = WM_menu_invoke;
5353 ot->exec = uv_select_similar_exec;
5355
5356 /* flags */
5357 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5358
5359 /* properties */
5360 PropertyRNA *prop = ot->prop = RNA_def_enum(
5361 ot->srna, "type", uv_select_similar_type_items, SIMVERT_NORMAL, "Type", "");
5364 RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
5365 RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
5366}
5367
5369
5370/* -------------------------------------------------------------------- */
5376
5377BMFace **ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
5378{
5379 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5380
5381 CLAMP_MAX(len_max, bm->totface);
5382 int faces_len = 0;
5383 BMFace **faces = MEM_malloc_arrayN<BMFace *>(len_max, __func__);
5384
5385 BMIter iter;
5386 BMFace *f;
5387 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5388 if (uvedit_face_visible_test(scene, f)) {
5389 if (uvedit_face_select_test(scene, f, offsets)) {
5390 faces[faces_len++] = f;
5391 if (faces_len == len_max) {
5392 goto finally;
5393 }
5394 }
5395 }
5396 }
5397
5398finally:
5399 *r_faces_len = faces_len;
5400 if (faces_len != len_max) {
5401 faces = static_cast<BMFace **>(MEM_reallocN(faces, sizeof(*faces) * faces_len));
5402 }
5403 return faces;
5404}
5405
5406BMLoop **ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
5407{
5408 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5409 BLI_assert(offsets.uv >= 0);
5410
5411 CLAMP_MAX(len_max, bm->totloop);
5412 int edges_len = 0;
5413 BMLoop **edges = MEM_malloc_arrayN<BMLoop *>(len_max, __func__);
5414
5415 BMIter iter;
5416 BMFace *f;
5417
5418 /* Clear tag. */
5419 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5420 BMIter liter;
5421 BMLoop *l_iter;
5422 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
5424 }
5425 }
5426
5427 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5428 if (uvedit_face_visible_test(scene, f)) {
5429 BMIter liter;
5430 BMLoop *l_iter;
5431 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
5432 if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
5433 if (uvedit_edge_select_test(scene, l_iter, offsets)) {
5435
5436 edges[edges_len++] = l_iter;
5437 if (edges_len == len_max) {
5438 goto finally;
5439 }
5440
5441 /* Tag other connected loops so we don't consider them separate edges. */
5442 if (l_iter != l_iter->radial_next) {
5443 BMLoop *l_radial_iter = l_iter->radial_next;
5444 do {
5445 if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, offsets.uv)) {
5446 BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG);
5447 }
5448 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
5449 }
5450 }
5451 }
5452 }
5453 }
5454 }
5455
5456finally:
5457 *r_edges_len = edges_len;
5458 if (edges_len != len_max) {
5459 edges = static_cast<BMLoop **>(MEM_reallocN(edges, sizeof(*edges) * edges_len));
5460 }
5461 return edges;
5462}
5463
5464BMLoop **ED_uvedit_selected_verts(const Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
5465{
5466 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5467 BLI_assert(offsets.select_vert >= 0);
5468 BLI_assert(offsets.uv >= 0);
5469
5470 CLAMP_MAX(len_max, bm->totloop);
5471 int verts_len = 0;
5472 BMLoop **verts = MEM_malloc_arrayN<BMLoop *>(len_max, __func__);
5473
5474 BMIter iter;
5475 BMFace *f;
5476
5477 /* Clear tag. */
5478 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5479 BMIter liter;
5480 BMLoop *l_iter;
5481 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
5483 }
5484 }
5485
5486 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5487 if (uvedit_face_visible_test(scene, f)) {
5488 BMIter liter;
5489 BMLoop *l_iter;
5490 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
5491 if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
5492 if (BM_ELEM_CD_GET_BOOL(l_iter, offsets.select_vert)) {
5494
5495 verts[verts_len++] = l_iter;
5496 if (verts_len == len_max) {
5497 goto finally;
5498 }
5499
5500 /* Tag other connected loops so we don't consider them separate vertices. */
5501 BMIter liter_disk;
5502 BMLoop *l_disk_iter;
5503 BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) {
5504 if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, offsets.uv)) {
5505 BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG);
5506 }
5507 }
5508 }
5509 }
5510 }
5511 }
5512 }
5513
5514finally:
5515 *r_verts_len = verts_len;
5516 if (verts_len != len_max) {
5517 verts = static_cast<BMLoop **>(MEM_reallocN(verts, sizeof(*verts) * verts_len));
5518 }
5519 return verts;
5520}
5521
5523
5524/* -------------------------------------------------------------------- */
5527
5533static void uv_isolate_selected_islands(const Scene *scene, BMesh *bm, const BMUVOffsets &offsets)
5534{
5536 BMFace *efa;
5537 BMIter iter, liter;
5538 UvElementMap *elementmap = BM_uv_element_map_create(bm, scene, false, false, true, true);
5539 if (elementmap == nullptr) {
5540 return;
5541 }
5542 BLI_assert(offsets.select_vert >= 0);
5543 BLI_assert(offsets.select_edge >= 0);
5544
5545 int num_islands = elementmap->total_islands;
5546 /* Boolean array that tells if island with index i is completely selected or not. */
5547 bool *is_island_not_selected = MEM_calloc_arrayN<bool>((num_islands), __func__);
5548
5549 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5550 BMLoop *l;
5551 if (!uvedit_face_visible_test(scene, efa)) {
5553 continue;
5554 }
5556 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5557 if (!uvedit_edge_select_test(scene, l, offsets)) {
5558 UvElement *element = BM_uv_element_get(elementmap, l);
5559 if (element) {
5560 is_island_not_selected[element->island] = true;
5561 }
5562 }
5563 }
5564 }
5565
5566 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5567 BMLoop *l;
5568 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
5569 continue;
5570 }
5571 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5572 UvElement *element = BM_uv_element_get(elementmap, l);
5573 /* Deselect all elements of islands which are not completely selected. */
5574 if (element && is_island_not_selected[element->island]) {
5575 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
5576 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
5577 }
5578 }
5579 }
5580
5581 BM_uv_element_map_free(elementmap);
5582 MEM_freeN(is_island_not_selected);
5583}
5584
5585void ED_uvedit_selectmode_clean(const Scene *scene, Object *obedit)
5586{
5587 const ToolSettings *ts = scene->toolsettings;
5588 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
5589 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
5590 char sticky = ts->uv_sticky;
5591
5593 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
5594 BMFace *efa;
5595 BMLoop *l;
5596 BMIter iter, liter;
5597
5598 if (ts->uv_selectmode == UV_SELECT_VERTEX) {
5599 /* Vertex mode. */
5600 if (sticky != SI_STICKY_DISABLE) {
5602 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5603 if (!uvedit_face_visible_test(scene, efa)) {
5604 continue;
5605 }
5606 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5607 if (uvedit_uv_select_test(scene, l, offsets)) {
5609 }
5610 }
5611 }
5612 uv_select_flush_from_tag_loop(scene, obedit, true);
5613 }
5614 }
5615
5616 else if (ts->uv_selectmode == UV_SELECT_EDGE) {
5617 /* Edge mode. */
5618 if (sticky != SI_STICKY_DISABLE) {
5619 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5620 if (!uvedit_face_visible_test(scene, efa)) {
5621 continue;
5622 }
5623 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5624 if (uvedit_edge_select_test(scene, l, offsets)) {
5625 uvedit_edge_select_set_noflush(scene, l, true, sticky, offsets);
5626 }
5627 }
5628 }
5629 }
5631 }
5632
5633 else if (ts->uv_selectmode == UV_SELECT_FACE) {
5634 /* Face mode. */
5635 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
5637 if (uvedit_face_visible_test(scene, efa)) {
5638 if (uvedit_face_select_test(scene, efa, offsets)) {
5640 }
5641 uvedit_face_select_set(scene, bm, efa, false, offsets);
5642 }
5643 }
5644 uv_select_flush_from_tag_face(scene, obedit, true);
5645 }
5646
5647 else if (ts->uv_selectmode == UV_SELECT_ISLAND) {
5648 /* Island mode. */
5649 uv_isolate_selected_islands(scene, bm, offsets);
5650 }
5651
5653}
5654
5656{
5657 Scene *scene = CTX_data_scene(C);
5658 ToolSettings *ts = scene->toolsettings;
5659 ViewLayer *view_layer = CTX_data_view_layer(C);
5661
5663 scene, view_layer, nullptr);
5664 for (Object *obedit : objects) {
5665 ED_uvedit_selectmode_clean(scene, obedit);
5666
5668 }
5669}
5670
5672{
5673 Scene *scene = CTX_data_scene(C);
5674 ToolSettings *ts = scene->toolsettings;
5675 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
5676 return;
5677 }
5678 /* Only for edge/face select modes. */
5679 if (ts->selectmode & SCE_SELECT_VERTEX) {
5680 return;
5681 }
5682
5683 ViewLayer *view_layer = CTX_data_view_layer(C);
5685
5687 scene, view_layer, nullptr);
5688 for (Object *obedit : objects) {
5690 }
5691}
5692
5694{
5695 Scene *scene = CTX_data_scene(C);
5696 ToolSettings *ts = scene->toolsettings;
5697 const char new_uv_selectmode = RNA_enum_get(op->ptr, "type");
5698
5699 /* Early exit if no change in current selection mode */
5700 if (new_uv_selectmode == ts->uv_selectmode) {
5701 return OPERATOR_CANCELLED;
5702 }
5703
5704 /* Set new UV select mode. */
5705 ts->uv_selectmode = new_uv_selectmode;
5706
5707 /* Handle UV selection states according to new select mode and sticky mode. */
5709
5712
5713 return OPERATOR_FINISHED;
5714}
5715
5717 wmOperator *op,
5718 const wmEvent * /*event*/)
5719{
5721 const SpaceImage *sima = CTX_wm_space_image(C);
5722
5723 /* Could be removed? - Already done in poll callback. */
5724 if ((!sima) || (sima->mode != SI_MODE_UV)) {
5725 return OPERATOR_CANCELLED;
5726 }
5727 /* Pass through when UV sync selection is enabled.
5728 * Allow for mesh select-mode key-map. */
5729 if (ts->uv_flag & UV_SYNC_SELECTION) {
5730 return OPERATOR_PASS_THROUGH;
5731 }
5732
5733 return uv_select_mode_exec(C, op);
5734}
5735
5737{
5738 /* identifiers */
5739 ot->name = "UV Select Mode";
5740 ot->description = "Change UV selection mode";
5741 ot->idname = "UV_OT_select_mode";
5742
5743 /* API callbacks. */
5744 ot->invoke = uv_select_mode_invoke;
5745 ot->exec = uv_select_mode_exec;
5747
5748 /* flags */
5749 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5750
5751 /* RNA props */
5752 PropertyRNA *prop;
5753 ot->prop = prop = RNA_def_enum(
5754 ot->srna, "type", rna_enum_mesh_select_mode_uv_items, 0, "Type", "");
5756}
5757
SpaceImage * CTX_wm_space_image(const bContext *C)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc: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
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#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 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:1009
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ UV_SYNC_SELECTION
@ SI_STICKY_VERTEX
@ SI_STICKY_LOC
@ SI_STICKY_DISABLE
@ UV_SELECT_VERTEX
@ UV_SELECT_FACE
@ UV_SELECT_EDGE
@ UV_SELECT_ISLAND
@ 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)
UvVertMap * BM_uv_vert_map_create(BMesh *bm, bool use_select)
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
UvElement * BM_uv_element_get(const UvElementMap *element_map, const BMLoop *l)
void EDBM_select_toggle_all(BMEditMesh *em)
void EDBM_select_more(BMEditMesh *em, bool use_face_step)
void EDBM_select_swap(BMEditMesh *em)
void BM_uv_element_map_free(UvElementMap *element_map)
UvElementMap * BM_uv_element_map_create(BMesh *bm, const Scene *scene, bool uv_selected, bool use_winding, bool use_seams, bool do_islands)
void EDBM_selectmode_flush(BMEditMesh *em)
UvMapVert * BM_uv_vert_map_at_index(UvVertMap *vmap, unsigned int v)
void EDBM_flag_enable_all(BMEditMesh *em, char hflag)
void EDBM_select_less(BMEditMesh *em, bool use_face_step)
void 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)
#define SEL_OP_USE_PRE_DESELECT(sel_op)
std::string ED_select_circle_get_name(wmOperatorType *ot, PointerRNA *ptr)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
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)
bool uvedit_face_select_test(const Scene *scene, const BMFace *efa, const BMUVOffsets &offsets)
bool uvedit_face_select_test_ex(const ToolSettings *ts, const BMFace *efa, const BMUVOffsets &offsets)
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)
bool uvedit_face_visible_test_ex(const ToolSettings *ts, const BMFace *efa)
bool uvedit_edge_select_test_ex(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets &offsets)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#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:1750
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:1701
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:1738
float UI_view2d_scale_get_y(const View2D *v2d)
Definition view2d.cc:1924
void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1911
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:1667
#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:1674
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1920
#define NC_GEOM
Definition WM_types.hh:390
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_SCENE
Definition WM_types.hh:375
#define ND_TOOLSETTINGS
Definition WM_types.hh:446
#define ND_SPACE_IMAGE
Definition WM_types.hh:519
#define ND_SELECT
Definition WM_types.hh:505
#define NC_SPACE
Definition WM_types.hh:389
#define U
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#define BM_ELEM_CD_SET_BOOL(ele, offset, f)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_test_bool(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_uv_map_attr_edge_select_ensure(BMesh *bm, const StringRef uv_map_name)
bool BM_uv_map_attr_pin_exists(const BMesh *bm, const StringRef uv_map_name)
void BM_uv_map_attr_pin_ensure(BMesh *bm, const StringRef uv_map_name)
void BM_uv_map_attr_vert_select_ensure(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_mesh_select_flush(BMesh *bm)
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_deselect_flush(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_active_face_set(BMesh *bm, BMFace *f)
#define BM_select_history_store_notest(bm, ele)
#define BM_select_history_store(bm, ele)
#define BM_select_history_remove(bm, ele)
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
ATTR_WARN_UNUSED_RESULT const void * element
@ 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)
BPy_StructRNA * depsgraph
bool is_empty() const
Definition BLI_array.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
IndexRange index_range() const
#define sqrtf(x)
static const EnumPropertyItem prop_similar_compare_types[]
static ushort indices[]
static float verts[][3]
#define select(A, B, C)
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
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
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
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]
CustomData ldata
BMFace ** ftable
BMFace ** faces
Definition ED_uvedit.hh:297
BMUVOffsets offsets
Definition ED_uvedit.hh:303
Definition DNA_ID.h:404
struct ToolSettings * toolsettings
float tri[3][2]
unsigned int face_index
UvMapVert * next
int mval[2]
Definition WM_types.hh:760
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
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 UV_OT_select_all(wmOperatorType *ot)
bool uvedit_face_select_test(const Scene *scene, const BMFace *efa, const BMUVOffsets &offsets)
void UV_OT_select_edge_ring(wmOperatorType *ot)
bool uvedit_edge_is_face_select_any_other(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets &offsets)
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)
void ED_uvedit_selectmode_clean(const Scene *scene, Object *obedit)
UV Select Mode set.
#define SET_SELECTION(value)
void ED_uvedit_selectmode_clean_multi(bContext *C)
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)
const float * uvedit_first_selected_uv_from_vertex(Scene *scene, BMVert *eve, const BMUVOffsets &offsets)
static void uv_select_tag_update_for_object(Depsgraph *depsgraph, const ToolSettings *ts, Object *obedit)
static wmOperatorStatus uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick)
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 uvedit_face_select_disable(const Scene *scene, BMesh *bm, BMFace *efa, const BMUVOffsets &offsets)
void ED_uvedit_sticky_selectmode_update(bContext *C)
void UV_OT_select(wmOperatorType *ot)
void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
void UV_OT_select_split(wmOperatorType *ot)
static 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)
void UV_OT_select_linked(wmOperatorType *ot)
bool uvedit_vert_is_edge_select_any_other(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets &offsets)
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)
BMLoop ** ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
void uvedit_uv_select_enable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets &offsets)
bool uvedit_face_select_test_ex(const ToolSettings *ts, const BMFace *efa, const BMUVOffsets &offsets)
static void uv_select_all_perform(const Scene *scene, Object *obedit, int action)
BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v)
void uvedit_edge_select_set(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
Select UV Edge.
static wmOperatorStatus uv_select_more_exec(bContext *C, wmOperator *)
char ED_uvedit_select_mode_get(const Scene *scene)
void ED_uvedit_selectmode_flush(const Scene *scene, BMesh *bm)
UV Select Mode Flush.
bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
#define NEIGHBORING_FACE_IS_SEL
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)
bool uvedit_uv_select_test(const Scene *scene, const BMLoop *l, const BMUVOffsets &offsets)
void uvedit_deselect_flush(const Scene *scene, BMesh *bm)
static wmOperatorStatus uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void uvedit_uv_select_disable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets &offsets)
bool uvedit_select_is_any_selected_multi(const Scene *scene, const Span< Object * > objects)
void UV_OT_select_circle(wmOperatorType *ot)
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 UV_OT_select_mode(wmOperatorType *ot)
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d)
static wmOperatorStatus uv_circle_select_exec(bContext *C, wmOperator *op)
void UV_OT_select_similar(wmOperatorType *ot)
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)
void uvedit_edge_select_disable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets &offsets)
static wmOperatorStatus uv_select_similar_island_exec(bContext *C, wmOperator *op)
bool uvedit_edge_select_test(const Scene *scene, const BMLoop *l, const BMUVOffsets &offsets)
void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMesh *bm, const bool select)
void UV_OT_select_linked_pick(wmOperatorType *ot)
static wmOperatorStatus uv_select_exec(bContext *C, wmOperator *op)
void UV_OT_select_more(wmOperatorType *ot)
BMLoop * uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, const float co[2])
static wmOperatorStatus uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void uv_select_flush_from_tag_sticky_loc_internal(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
static wmOperatorStatus uv_select_mode_exec(bContext *C, wmOperator *op)
void UV_OT_select_pinned(wmOperatorType *ot)
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)
static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
static const EnumPropertyItem * uv_select_similar_type_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
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)
BMLoop * ED_uvedit_active_edge_loop_get(BMesh *bm)
static bool do_lasso_select_mesh_uv(bContext *C, const Span< int2 > mcoords, const eSelectOp sel_op)
static void uv_isolate_selected_islands(const Scene *scene, BMesh *bm, const BMUVOffsets &offsets)
void UV_OT_select_loop(wmOperatorType *ot)
static void uv_select_all_perform_multi_ex(const Scene *scene, Span< Object * > objects, int action, const Object *ob_exclude)
BLI_INLINE uint overlap_hash(const void *overlap_v)
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)
void uvedit_face_select_set(const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const BMUVOffsets &offsets)
Select UV Face.
static void bm_clear_uv_vert_selection(const Scene *scene, BMesh *bm, const BMUVOffsets &offsets)
static void uvedit_vertex_select_tagged(BMesh *bm, Scene *scene, bool select, const BMUVOffsets &offsets)
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)
#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 BMLoop *l, const BMUVOffsets &offsets)
void uvedit_face_select_enable(const Scene *scene, BMesh *bm, BMFace *efa, const BMUVOffsets &offsets)
static wmOperatorStatus uv_lasso_select_exec(bContext *C, wmOperator *op)
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)
BMLoop * ED_uvedit_active_vert_loop_get(BMesh *bm)
static wmOperatorStatus uv_select_linked_pick_exec(bContext *C, wmOperator *op)
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])
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)
void uvedit_edge_select_set_noflush(const Scene *scene, BMLoop *l, const bool select, const int sticky_flag, const BMUVOffsets &offsets)
static BMLoop * uvedit_loop_find_other_radial_loop_with_visible_face(const Scene *scene, BMLoop *l_src, const BMUVOffsets &offsets)
void UV_OT_select_overlap(wmOperatorType *ot)
void uvedit_edge_select_enable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets &offsets)
void uvedit_face_select_shared_vert(const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const BMUVOffsets &offsets)
void UV_OT_select_lasso(wmOperatorType *ot)
static bool uv_island_selected(const Scene *scene, FaceIsland *island)
static bool uv_mouse_select_multi(bContext *C, const Span< Object * > objects, const float co[2], const SelectPick_Params &params)
void uvedit_uv_select_set(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const BMUVOffsets &offsets)
Select UV Vertex.
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)
bool uvedit_select_is_any_selected(const Scene *scene, BMesh *bm)
void uvedit_select_prepare_custom_data(const Scene *scene, BMesh *bm)
bool uvedit_uv_select_test_ex(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets &offsets)
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)
static wmOperatorStatus uv_select_less_exec(bContext *C, wmOperator *)
BMFace ** ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMesh *bm)
static wmOperatorStatus uv_select_similar_vert_exec(bContext *C, wmOperator *op)
bool uvedit_vert_is_face_select_any_other(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets &offsets)
bool uvedit_face_visible_test_ex(const ToolSettings *ts, const BMFace *efa)
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)
void uvedit_select_flush(const Scene *scene, BMesh *bm)
bool uvedit_edge_select_test_ex(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets &offsets)
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 UV_OT_select_less(wmOperatorType *ot)
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)
static void uv_select_toggle_all(const Scene *scene, BMEditMesh *em)
static wmOperatorStatus uv_select_overlap(bContext *C, const bool extend)
void UV_OT_select_box(wmOperatorType *ot)
static void uv_select_linked_multi(Scene *scene, const Span< Object * > objects, UvNearestHit *hit, const bool extend, bool deselect, const bool toggle, const bool select_faces)
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4225
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:139