Blender V4.3
uvedit_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cmath>
10#include <cstdlib>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_image_types.h"
16#include "DNA_material_types.h"
17#include "DNA_mesh_types.h"
18#include "DNA_node_types.h"
19#include "DNA_object_types.h"
20#include "DNA_scene_types.h"
21#include "DNA_space_types.h"
22
23#include "BLI_kdtree.h"
24#include "BLI_math_base.hh"
25#include "BLI_math_geom.h"
26#include "BLI_math_vector.h"
27#include "BLI_math_vector.hh"
28#include "BLI_utildefines.h"
29
30#include "BLT_translation.hh"
31
32#include "BKE_context.hh"
33#include "BKE_customdata.hh"
34#include "BKE_editmesh.hh"
35#include "BKE_layer.hh"
36#include "BKE_material.h"
37#include "BKE_mesh_mapping.hh"
38#include "BKE_mesh_types.hh"
39#include "BKE_node.hh"
40
41#include "DEG_depsgraph.hh"
43
44#include "ED_image.hh"
45#include "ED_mesh.hh"
46#include "ED_node.hh"
47#include "ED_screen.hh"
48#include "ED_uvedit.hh"
49
50#include "RNA_access.hh"
51#include "RNA_define.hh"
52
53#include "WM_api.hh"
54#include "WM_message.hh"
55#include "WM_types.hh"
56
57#include "UI_interface.hh"
58#include "UI_resources.hh"
59#include "UI_view2d.hh"
60
61#include "uvedit_intern.hh"
62
63using namespace blender;
64
65/* -------------------------------------------------------------------- */
70{
71 BMEditMesh *em;
72 int ret;
73
74 if (!obedit) {
75 return false;
76 }
77
78 if (obedit->type != OB_MESH) {
79 return false;
80 }
81
82 em = BKE_editmesh_from_object(obedit);
83 ret = EDBM_uv_check(em);
84
85 return ret;
86}
87
89{
91
92 if (ob && ob->type == OB_MESH) {
93 Mesh *mesh = static_cast<Mesh *>(ob->data);
94
95 if (CustomData_get_layer(&mesh->corner_data, CD_PROP_FLOAT2) != nullptr) {
96 return 1;
97 }
98 }
99
100 return 0;
101}
102
105/* -------------------------------------------------------------------- */
109static bool is_image_texture_node(bNode *node)
110{
112}
113
115 int mat_nr,
116 Image **r_ima,
117 ImageUser **r_iuser,
118 const bNode **r_node,
119 const bNodeTree **r_ntree)
120{
122 BKE_object_material_get(ob, mat_nr);
123 bNodeTree *ntree = (ma && ma->use_nodes) ? ma->nodetree : nullptr;
124 bNode *node = (ntree) ? bke::node_get_active_texture(ntree) : nullptr;
125
126 if (node && is_image_texture_node(node)) {
127 if (r_ima) {
128 *r_ima = (Image *)node->id;
129 }
130 if (r_iuser) {
131 if (node->type == SH_NODE_TEX_IMAGE) {
132 *r_iuser = &((NodeTexImage *)node->storage)->iuser;
133 }
134 else if (node->type == SH_NODE_TEX_ENVIRONMENT) {
135 *r_iuser = &((NodeTexEnvironment *)node->storage)->iuser;
136 }
137 else {
138 *r_iuser = nullptr;
139 }
140 }
141 if (r_node) {
142 *r_node = node;
143 }
144 if (r_ntree) {
145 *r_ntree = ntree;
146 }
147 return true;
148 }
149
150 if (r_ima) {
151 *r_ima = nullptr;
152 }
153 if (r_iuser) {
154 *r_iuser = nullptr;
155 }
156 if (r_node) {
157 *r_node = node;
158 }
159 if (r_ntree) {
160 *r_ntree = ntree;
161 }
162
163 return false;
164}
165
166void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima)
167{
168 Material *ma = BKE_object_material_get(ob, mat_nr);
169 bNode *node = (ma && ma->use_nodes) ? bke::node_get_active_texture(ma->nodetree) : nullptr;
170
171 if (node && is_image_texture_node(node)) {
172 node->id = &ima->id;
173 ED_node_tree_propagate_change(nullptr, bmain, ma->nodetree);
174 }
175}
176
179/* -------------------------------------------------------------------- */
184{
185 if (sima && (sima->flag & SI_LIVE_UNWRAP)) {
186 ED_uvedit_live_unwrap_begin(scene, obedit, nullptr);
189 }
190}
191
194/* -------------------------------------------------------------------- */
198void ED_uvedit_foreach_uv(const Scene *scene,
199 BMesh *bm,
200 const bool skip_invisible,
201 const bool selected,
202 FunctionRef<void(float[2])> user_fn)
203{
204 /* Check selection for quick return. */
205 const bool synced_selection = (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
206 if (synced_selection && bm->totvertsel == (selected ? 0 : bm->totvert)) {
207 return;
208 }
209
210 BMFace *efa;
211 BMLoop *l;
212 BMIter iter, liter;
213
214 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
215
216 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
217 if (skip_invisible && !uvedit_face_visible_test(scene, efa)) {
218 continue;
219 }
220
221 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
222 if (uvedit_uv_select_test(scene, l, offsets) == selected) {
223 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
224 user_fn(luv);
225 }
226 }
227 }
228}
229
231 const Span<Object *> objects_edit,
232 const bool skip_invisible,
233 const bool skip_nonselected,
234 FunctionRef<void(float[2])> user_fn)
235{
236 for (Object *obedit : objects_edit) {
238 ED_uvedit_foreach_uv(scene, em->bm, skip_invisible, skip_nonselected, user_fn);
239 }
240}
241
243 const Span<Object *> objects_edit,
244 float r_min[2],
245 float r_max[2])
246{
247 bool changed = false;
248 INIT_MINMAX2(r_min, r_max);
249 ED_uvedit_foreach_uv_multi(scene, objects_edit, true, true, [&](float luv[2]) {
250 minmax_v2v2_v2(r_min, r_max, luv);
251 changed = true;
252 });
253 return changed;
254}
255
257{
258 BMFace *efa;
259 BMLoop *l;
260 BMIter iter, liter;
261 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
262
263 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
264 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
265 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
266 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
267 }
268 }
269}
270
271static bool ED_uvedit_median_multi(const Scene *scene,
272 const Span<Object *> objects_edit,
273 float co[2])
274{
275 uint sel = 0;
276 zero_v2(co);
277
278 ED_uvedit_foreach_uv_multi(scene, objects_edit, true, true, [&](float luv[2]) {
279 add_v2_v2(co, luv);
280 sel++;
281 });
282
283 mul_v2_fl(co, 1.0f / float(sel));
284
285 return (sel != 0);
286}
287
289 Span<Object *> objects_edit,
290 float cent[2],
291 char mode)
292{
293 bool changed = false;
294
295 if (mode == V3D_AROUND_CENTER_BOUNDS) { /* bounding box */
296 float min[2], max[2];
297 if (ED_uvedit_minmax_multi(scene, objects_edit, min, max)) {
298 mid_v2_v2v2(cent, min, max);
299 changed = true;
300 }
301 }
302 else {
303 if (ED_uvedit_median_multi(scene, objects_edit, cent)) {
304 changed = true;
305 }
306 }
307
308 return changed;
309}
310
312 Scene *scene,
313 ViewLayer *view_layer,
314 float r_center[2],
315 char mode,
316 bool *r_has_select)
317{
318 bool changed = false;
319 switch (mode) {
320 case V3D_AROUND_CURSOR: {
321 copy_v2_v2(r_center, sima->cursor);
322 changed = true;
323 if (r_has_select != nullptr) {
324 Vector<Object *> objects =
326 scene, view_layer, nullptr);
327 *r_has_select = uvedit_select_is_any_selected_multi(scene, objects);
328 }
329 break;
330 }
331 default: {
332 Vector<Object *> objects =
334 scene, view_layer, nullptr);
335 changed = ED_uvedit_center_multi(scene, objects, r_center, mode);
336 if (r_has_select != nullptr) {
337 *r_has_select = changed;
338 }
339 break;
340 }
341 }
342 return changed;
343}
344
347/* -------------------------------------------------------------------- */
360
361static bool uvedit_uv_align_weld(Scene *scene,
362 BMesh *bm,
363 const eUVWeldAlign tool,
364 const float cent[2])
365{
366 bool changed = false;
367
368 ED_uvedit_foreach_uv(scene, bm, true, true, [&](float luv[2]) {
369 if (ELEM(tool, UV_ALIGN_X, UV_WELD)) {
370 if (luv[0] != cent[0]) {
371 luv[0] = cent[0];
372 changed = true;
373 }
374 }
375 if (ELEM(tool, UV_ALIGN_Y, UV_WELD)) {
376 if (luv[1] != cent[1]) {
377 luv[1] = cent[1];
378 changed = true;
379 }
380 }
381 });
382
383 return changed;
384}
385
389 UVEP_SELECTED = (1 << 0),
390 UVEP_PINNED = (1 << 1), /* i.e. Pinned verts are preferred to selected. */
391};
393
395{
397 if (pinned) {
398 precedence |= UVEP_PINNED;
399 }
400 return precedence;
401}
402
407static bool uvedit_line_update_endpoint(const float *luv,
408 const bool pinned,
409 float uv_a[2],
410 eUVEndPointPrecedence *prec_a,
411 float uv_b[2],
412 eUVEndPointPrecedence *prec_b)
413{
415
416 float len_sq_a = len_squared_v2v2(uv_a, luv);
417 float len_sq_b = len_squared_v2v2(uv_b, luv);
418
419 /* Caching the value of `len_sq_ab` is unlikely to be faster than recalculating.
420 * Profile before optimizing. */
421 float len_sq_ab = len_squared_v2v2(uv_a, uv_b);
422
423 if ((*prec_a < flags && 0.0f < len_sq_b) || (*prec_a == flags && len_sq_ab < len_sq_b)) {
424 *prec_a = flags;
425 copy_v2_v2(uv_a, luv);
426 return true;
427 }
428
429 if ((*prec_b < flags && 0.0f < len_sq_a) || (*prec_b == flags && len_sq_ab < len_sq_a)) {
430 *prec_b = flags;
431 copy_v2_v2(uv_b, luv);
432 return true;
433 }
434
435 return false;
436}
437
442static bool uvedit_uv_straighten_elements(const UvElement *element,
443 const int len,
444 const BMUVOffsets offsets,
445 const eUVWeldAlign tool)
446{
447 float uv_start[2];
448 float uv_end[2];
451
452 /* Find start and end of line. */
453 for (int i = 0; i < 10; i++) { /* Heuristic to prevent infinite loop. */
454 bool update = false;
455 for (int j = 0; j < len; j++) {
456 float *luv = BM_ELEM_CD_GET_FLOAT_P(element[j].l, offsets.uv);
457 bool pinned = BM_ELEM_CD_GET_BOOL(element[j].l, offsets.pin);
458 update |= uvedit_line_update_endpoint(luv, pinned, uv_start, &prec_start, uv_end, &prec_end);
459 }
460 if (!update) {
461 break;
462 }
463 }
464
465 if (prec_start == UVEP_INVALID || prec_end == UVEP_INVALID) {
466 return false; /* Unable to find two endpoints. */
467 }
468
469 float a = 0.0f; /* Similar to "slope". */
470 eUVWeldAlign tool_local = tool;
471
472 if (tool_local == UV_STRAIGHTEN_X) {
473 if (uv_start[1] == uv_end[1]) {
474 /* Caution, different behavior outside line segment. */
475 tool_local = UV_STRAIGHTEN;
476 }
477 else {
478 a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
479 }
480 }
481 else if (tool_local == UV_STRAIGHTEN_Y) {
482 if (uv_start[0] == uv_end[0]) {
483 /* Caution, different behavior outside line segment. */
484 tool_local = UV_STRAIGHTEN;
485 }
486 else {
487 a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
488 }
489 }
490
491 bool changed = false;
492 for (int j = 0; j < len; j++) {
493 float *luv = BM_ELEM_CD_GET_FLOAT_P(element[j].l, offsets.uv);
494 /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
495 * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
496 * Maybe this should be a BLI func? Or is it already existing?
497 * Could use interp_v2_v2v2, but not sure it's worth it here. */
498 if (tool_local == UV_STRAIGHTEN_X) {
499 luv[0] = a * (luv[1] - uv_start[1]) + uv_start[0];
500 }
501 else if (tool_local == UV_STRAIGHTEN_Y) {
502 luv[1] = a * (luv[0] - uv_start[0]) + uv_start[1];
503 }
504 else {
505 closest_to_line_segment_v2(luv, luv, uv_start, uv_end);
506 }
507 changed = true; /* TODO: Did the UV actually move? */
508 }
509 return changed;
510}
511
515static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool)
516{
517 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
518 if (offsets.uv == -1) {
519 return false;
520 }
521
522 UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true, true);
523 if (element_map == nullptr) {
524 return false;
525 }
526
527 bool changed = false;
528 for (int i = 0; i < element_map->total_islands; i++) {
529 changed |= uvedit_uv_straighten_elements(element_map->storage + element_map->island_indices[i],
530 element_map->island_total_uvs[i],
531 offsets,
532 tool);
533 }
534
535 BM_uv_element_map_free(element_map);
536 return changed;
537}
538
540{
541 Scene *scene = CTX_data_scene(C);
542 ViewLayer *view_layer = CTX_data_view_layer(C);
544 const ToolSettings *ts = scene->toolsettings;
545 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
546 float cent[2], min[2], max[2];
547
548 INIT_MINMAX2(min, max);
549
551 scene, view_layer, nullptr);
552
553 if (tool == UV_ALIGN_AUTO) {
555 scene, objects, true, true, [&](float luv[2]) { minmax_v2v2_v2(min, max, luv); });
556 tool = (max[0] - min[0] >= max[1] - min[1]) ? UV_ALIGN_Y : UV_ALIGN_X;
557 }
558
559 ED_uvedit_center_multi(scene, objects, cent, 0);
560
561 for (Object *obedit : objects) {
563 bool changed = false;
564
565 if (synced_selection && (em->bm->totvertsel == 0)) {
566 continue;
567 }
568
570 changed |= uvedit_uv_align_weld(scene, em->bm, tool, cent);
571 }
572
574 changed |= uvedit_uv_straighten(scene, em->bm, tool);
575 }
576
577 if (changed) {
578 uvedit_live_unwrap_update(sima, scene, obedit);
579 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
580 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
581 }
582 }
583}
584
586{
587 uv_weld_align(C, eUVWeldAlign(RNA_enum_get(op->ptr, "axis")));
588
589 return OPERATOR_FINISHED;
590}
591
593{
594 static const EnumPropertyItem axis_items[] = {
596 "ALIGN_S",
597 0,
598 "Straighten",
599 "Align UV vertices along the line defined by the endpoints"},
601 "ALIGN_T",
602 0,
603 "Straighten X",
604 "Align UV vertices, moving them horizontally to the line defined by the endpoints"},
606 "ALIGN_U",
607 0,
608 "Straighten Y",
609 "Align UV vertices, moving them vertically to the line defined by the endpoints"},
611 "ALIGN_AUTO",
612 0,
613 "Align Auto",
614 "Automatically choose the direction on which there is most alignment already"},
615 {UV_ALIGN_X, "ALIGN_X", 0, "Align Vertically", "Align UV vertices on a vertical line"},
616 {UV_ALIGN_Y, "ALIGN_Y", 0, "Align Horizontally", "Align UV vertices on a horizontal line"},
617 {0, nullptr, 0, nullptr, nullptr},
618 };
619
620 /* identifiers */
621 ot->name = "Align";
622 ot->description = "Aligns selected UV vertices on a line";
623 ot->idname = "UV_OT_align";
625
626 /* api callbacks */
629
630 /* properties */
632 ot->srna, "axis", axis_items, UV_ALIGN_AUTO, "Axis", "Axis to align UV locations on");
633}
634
637/* -------------------------------------------------------------------- */
642{
643 Scene *scene = CTX_data_scene(C);
644 ViewLayer *view_layer = CTX_data_view_layer(C);
646 const ToolSettings *ts = scene->toolsettings;
647
648 const float threshold = RNA_float_get(op->ptr, "threshold");
649 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
650
652 scene, view_layer, nullptr);
653
654 bool *changed = static_cast<bool *>(MEM_callocN(sizeof(bool) * objects.size(), __func__));
655
656 /* Maximum index of an objects[i]'s UVs in UV_arr.
657 * It helps find which UV in *mloopuv_arr belongs to which object. */
658 uint *ob_mloopuv_max_idx = static_cast<uint *>(
659 MEM_callocN(sizeof(uint) * objects.size(), __func__));
660
661 /* Calculate max possible number of kdtree nodes. */
662 int uv_maxlen = 0;
663 for (Object *obedit : objects) {
665
666 if (synced_selection && (em->bm->totvertsel == 0)) {
667 continue;
668 }
669
670 uv_maxlen += em->bm->totloop;
671 }
672
673 KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
674
675 blender::Vector<int> duplicates;
676 blender::Vector<float *> mloopuv_arr;
677
678 int mloopuv_count = 0; /* Also used for *duplicates count. */
679
680 for (const int ob_index : objects.index_range()) {
681 Object *obedit = objects[ob_index];
683 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
684 BLI_kdtree_2d_insert(tree, mloopuv_count, luv);
685 duplicates.append(-1);
686 mloopuv_arr.append(luv);
687 mloopuv_count++;
688 });
689
690 ob_mloopuv_max_idx[ob_index] = mloopuv_count - 1;
691 }
692
693 BLI_kdtree_2d_balance(tree);
694 int found_duplicates = BLI_kdtree_2d_calc_duplicates_fast(
695 tree, threshold, false, duplicates.data());
696
697 if (found_duplicates > 0) {
698 /* Calculate average uv for duplicates. */
699 int *uv_duplicate_count = static_cast<int *>(
700 MEM_callocN(sizeof(int) * mloopuv_count, __func__));
701 for (int i = 0; i < mloopuv_count; i++) {
702 if (duplicates[i] == -1) { /* If doesn't reference another */
703 uv_duplicate_count[i]++; /* self */
704 continue;
705 }
706
707 if (duplicates[i] != i) {
708 /* If not self then accumulate uv for averaging.
709 * Self uv is already present in accumulator */
710 add_v2_v2(mloopuv_arr[duplicates[i]], mloopuv_arr[i]);
711 }
712 uv_duplicate_count[duplicates[i]]++;
713 }
714
715 for (int i = 0; i < mloopuv_count; i++) {
716 if (uv_duplicate_count[i] < 2) {
717 continue;
718 }
719
720 mul_v2_fl(mloopuv_arr[i], 1.0f / float(uv_duplicate_count[i]));
721 }
722 MEM_freeN(uv_duplicate_count);
723
724 /* Update duplicated uvs. */
725 uint ob_index = 0;
726 for (int i = 0; i < mloopuv_count; i++) {
727 /* Make sure we know which object owns the mloopuv at this index.
728 * Remember that in some cases the object will have no loop uv,
729 * thus we need the while loop, and not simply an if check. */
730 while (ob_mloopuv_max_idx[ob_index] < i) {
731 ob_index++;
732 }
733
734 if (duplicates[i] == -1) {
735 continue;
736 }
737
738 copy_v2_v2(mloopuv_arr[i], mloopuv_arr[duplicates[i]]);
739 changed[ob_index] = true;
740 }
741
742 for (ob_index = 0; ob_index < objects.size(); ob_index++) {
743 if (changed[ob_index]) {
744 Object *obedit = objects[ob_index];
745 uvedit_live_unwrap_update(sima, scene, obedit);
746 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
748 }
749 }
750 }
751
752 BLI_kdtree_2d_free(tree);
753 MEM_freeN(changed);
754 MEM_freeN(ob_mloopuv_max_idx);
755
756 return OPERATOR_FINISHED;
757}
758
760{
761 Scene *scene = CTX_data_scene(C);
762 ViewLayer *view_layer = CTX_data_view_layer(C);
764 const float threshold = RNA_float_get(op->ptr, "threshold");
765
767 scene, view_layer, nullptr);
768
769 /* Calculate max possible number of kdtree nodes. */
770 int uv_maxlen = 0;
771 for (Object *obedit : objects) {
773 uv_maxlen += em->bm->totloop;
774 }
775
776 KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
777
778 blender::Vector<float *> mloopuv_arr;
779
780 int mloopuv_count = 0;
781
782 /* Add visible non-selected uvs to tree */
783 ED_uvedit_foreach_uv_multi(scene, objects, true, false, [&](float luv[2]) {
784 BLI_kdtree_2d_insert(tree, mloopuv_count, luv);
785 mloopuv_arr.append(luv);
786 mloopuv_count++;
787 });
788
789 BLI_kdtree_2d_balance(tree);
790
791 /* For each selected uv, find duplicate non selected uv. */
792 for (Object *obedit : objects) {
793 bool changed = false;
795 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
796 KDTreeNearest_2d nearest;
797 const int i = BLI_kdtree_2d_find_nearest(tree, luv, &nearest);
798
799 if (i != -1 && nearest.dist < threshold) {
800 copy_v2_v2(luv, mloopuv_arr[i]);
801 changed = true;
802 }
803 });
804
805 if (changed) {
806 uvedit_live_unwrap_update(sima, scene, obedit);
807 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
808 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
809 }
810 }
811
812 BLI_kdtree_2d_free(tree);
813
814 return OPERATOR_FINISHED;
815}
816
818{
819 /* NOTE: The calculation for the center-point of loops belonging to a vertex will be skewed
820 * if one UV coordinate holds more loops than the others. */
821
822 Scene *scene = CTX_data_scene(C);
824 ViewLayer *view_layer = CTX_data_view_layer(C);
826 scene, view_layer, nullptr);
827
828 /* Only use the squared distance, to avoid a square-root. */
829 const float threshold_sq = math::square(RNA_float_get(op->ptr, "threshold"));
830
831 for (Object *obedit : objects) {
833 BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
834 BMVert *v;
835 BMLoop *l;
836 BMIter viter, liter;
837
838 /* The `changed` variable keeps track if any loops from the current object are merged. */
840 uvs.reserve(32);
841 bool changed = false;
842
843 BM_ITER_MESH (v, &viter, em->bm, BM_VERTS_OF_MESH) {
844
845 BLI_assert(uvs.size() == 0);
846 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
847 if (uvedit_uv_select_test(scene, l, offsets)) {
848 uvs.append(BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv));
849 }
850 }
851 if (uvs.size() <= 1) {
852 uvs.clear();
853 continue;
854 }
855
856 while (uvs.size() > 1) {
857 const int uvs_num = uvs.size();
858 float2 uv_average = {0.0f, 0.0f};
859 for (const float *luv : uvs) {
860 uv_average += float2(luv);
861 }
862 uv_average /= uvs_num;
863
864 /* Find the loop closest to the uv_average. This loop will be the base that all
865 * other loop's distances are calculated from. */
866
867 float dist_best_sq = math::distance_squared(uv_average, float2(uvs[0]));
868 float *uv_ref = uvs[0];
869 int uv_ref_index = 0;
870 for (int i = 1; i < uvs_num; i++) {
871 const float dist_test_sq = math::distance_squared(uv_average, float2(uvs[i]));
872 if (dist_test_sq < dist_best_sq) {
873 dist_best_sq = dist_test_sq;
874 uv_ref = uvs[i];
875 uv_ref_index = i;
876 }
877 }
878
879 const int uvs_end = uvs_num - 1;
880 std::swap(uvs[uv_ref_index], uvs[uvs_end]);
881
882 /* Move all the UVs within threshold to the end of the array. Sum of all UV coordinates
883 * within threshold is initialized with `uv_ref` coordinate data since while loop
884 * ends once it hits `uv_ref` UV. */
885 float2 uv_merged_average = {uv_ref[0], uv_ref[1]};
886 int i = 0;
887 int uvs_num_merged = 1;
888 while (uvs[i] != uv_ref && i < uvs_num - uvs_num_merged) {
889 const float dist_test_sq = len_squared_v2v2(uv_ref, uvs[i]);
890 if (dist_test_sq < threshold_sq) {
891 uv_merged_average += float2(uvs[i]);
892 std::swap(uvs[i], uvs[uvs_end - uvs_num_merged]);
893 uvs_num_merged++;
894 if (dist_test_sq != 0.0f) {
895 changed = true;
896 }
897 }
898 else {
899 i++;
900 }
901 }
902
903 /* Recalculate `uv_average` so it only considers UV's that are being included in merge
904 * operation. Then Shift all loops to that position. */
905 if (uvs_num_merged > 1) {
906 uv_merged_average /= uvs_num_merged;
907
908 for (int j = uvs_num - uvs_num_merged; j < uvs_num; j++) {
909 copy_v2_v2(uvs[j], uv_merged_average);
910 }
911 }
912
913 uvs.resize(uvs_num - uvs_num_merged);
914 }
915 uvs.clear();
916 }
917 if (changed) {
918 uvedit_live_unwrap_update(sima, scene, obedit);
919 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
920 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
921 }
922 }
923
924 return OPERATOR_FINISHED;
925}
926
928{
929 if (RNA_boolean_get(op->ptr, "use_unselected")) {
931 }
932 if (RNA_boolean_get(op->ptr, "use_shared_vertex")) {
934 }
935 return uv_remove_doubles_to_selected(C, op);
936}
937
939{
940 /* identifiers */
941 ot->name = "Merge UVs by Distance";
942 ot->description =
943 "Selected UV vertices that are within a radius of each other are welded together";
944 ot->idname = "UV_OT_remove_doubles";
946
947 /* api callbacks */
950
952 "threshold",
953 0.02f,
954 0.0f,
955 10.0f,
956 "Merge Distance",
957 "Maximum distance between welded vertices",
958 0.0f,
959 1.0f);
961 "use_unselected",
962 false,
963 "Unselected",
964 "Merge selected to other unselected vertices");
966 ot->srna, "use_shared_vertex", false, "Shared Vertex", "Weld UVs based on shared vertices");
967}
968
971/* -------------------------------------------------------------------- */
975static int uv_weld_exec(bContext *C, wmOperator * /*op*/)
976{
978
979 return OPERATOR_FINISHED;
980}
981
983{
984 /* identifiers */
985 ot->name = "Weld";
986 ot->description = "Weld selected UV vertices together";
987 ot->idname = "UV_OT_weld";
989
990 /* api callbacks */
993}
994
997/* -------------------------------------------------------------------- */
1001static void uv_snap_to_pixel(float uvco[2], float w, float h)
1002{
1003 uvco[0] = roundf(uvco[0] * w) / w;
1004 uvco[1] = roundf(uvco[1] * h) / h;
1005}
1006
1008{
1009 int width = 0, height = 0;
1010
1011 ED_space_image_get_size(sima, &width, &height);
1012 uv_snap_to_pixel(sima->cursor, width, height);
1013}
1014
1016 Span<Object *> objects_edit,
1017 SpaceImage *sima)
1018{
1019 return ED_uvedit_center_multi(scene, objects_edit, sima->cursor, sima->around);
1020}
1021
1022static void uv_snap_cursor_to_origin(float uvco[2])
1023{
1024 uvco[0] = 0;
1025 uvco[1] = 0;
1026}
1027
1029{
1030 SpaceImage *sima = CTX_wm_space_image(C);
1031
1032 bool changed = false;
1033
1034 switch (RNA_enum_get(op->ptr, "target")) {
1035 case 0:
1037 changed = true;
1038 break;
1039 case 1: {
1040 Scene *scene = CTX_data_scene(C);
1041 ViewLayer *view_layer = CTX_data_view_layer(C);
1042
1043 Vector<Object *> objects =
1045 scene, view_layer, nullptr);
1046 changed = uv_snap_cursor_to_selection(scene, objects, sima);
1047 break;
1048 }
1049 case 2:
1051 changed = true;
1052 break;
1053 }
1054
1055 if (!changed) {
1056 return OPERATOR_CANCELLED;
1057 }
1058
1060
1061 return OPERATOR_FINISHED;
1062}
1063
1065{
1066 static const EnumPropertyItem target_items[] = {
1067 {0, "PIXELS", 0, "Pixels", ""},
1068 {1, "SELECTED", 0, "Selected", ""},
1069 {2, "ORIGIN", 0, "Origin", ""},
1070 {0, nullptr, 0, nullptr, nullptr},
1071 };
1072
1073 /* identifiers */
1074 ot->name = "Snap Cursor";
1075 ot->description = "Snap cursor to target type";
1076 ot->idname = "UV_OT_snap_cursor";
1078
1079 /* api callbacks */
1081 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
1082
1083 /* properties */
1085 ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1086}
1087
1090/* -------------------------------------------------------------------- */
1094static bool uv_snap_uvs_to_cursor(Scene *scene, Object *obedit, const float cursor[2])
1095{
1097 bool changed = false;
1098
1099 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1100 copy_v2_v2(luv, cursor);
1101 changed = true;
1102 });
1103
1104 return changed;
1105}
1106
1107static bool uv_snap_uvs_offset(Scene *scene, Object *obedit, const float offset[2])
1108{
1110 bool changed = false;
1111
1112 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1113 add_v2_v2(luv, offset);
1114 changed = true;
1115 });
1116
1117 return changed;
1118}
1119
1121{
1123 BMesh *bm = em->bm;
1124 BMFace *f;
1125 BMLoop *l, *lsub;
1126 BMIter iter, liter, lsubiter;
1127 float *luv;
1128 bool changed = false;
1129 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1130
1131 /* Index every vert that has a selected UV using it, but only once so as to
1132 * get unique indices and to count how much to `malloc`. */
1133 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1134 if (uvedit_face_visible_test(scene, f)) {
1136 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1138 }
1139 }
1140 else {
1142 }
1143 }
1144
1145 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1146 if (BM_elem_flag_test(f, BM_ELEM_TAG)) { /* Face: visible. */
1147 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1148 if (BM_elem_flag_test(l, BM_ELEM_TAG)) { /* Loop: selected. */
1149 float uv[2] = {0.0f, 0.0f};
1150 int uv_tot = 0;
1151
1152 BM_ITER_ELEM (lsub, &lsubiter, l->v, BM_LOOPS_OF_VERT) {
1153 if (BM_elem_flag_test(lsub->f, BM_ELEM_TAG) && /* Face: visible. */
1154 !BM_elem_flag_test(lsub, BM_ELEM_TAG)) /* Loop: unselected. */
1155 {
1156 luv = BM_ELEM_CD_GET_FLOAT_P(lsub, offsets.uv);
1157 add_v2_v2(uv, luv);
1158 uv_tot++;
1159 }
1160 }
1161
1162 if (uv_tot) {
1163 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
1164 mul_v2_v2fl(luv, uv, 1.0f / float(uv_tot));
1165 changed = true;
1166 }
1167 }
1168 }
1169 }
1170 }
1171
1172 return changed;
1173}
1174
1175static bool uv_snap_uvs_to_pixels(SpaceImage *sima, Scene *scene, Object *obedit)
1176{
1178 int width = 0, height = 0;
1179 float w, h;
1180 bool changed = false;
1181
1182 ED_space_image_get_size(sima, &width, &height);
1183 w = float(width);
1184 h = float(height);
1185
1186 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1187 uv_snap_to_pixel(luv, w, h);
1188 changed = true;
1189 });
1190
1191 return changed;
1192}
1193
1195{
1196 Scene *scene = CTX_data_scene(C);
1197 ViewLayer *view_layer = CTX_data_view_layer(C);
1198 SpaceImage *sima = CTX_wm_space_image(C);
1199 const ToolSettings *ts = scene->toolsettings;
1200 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1201 const int target = RNA_enum_get(op->ptr, "target");
1202 float offset[2] = {0};
1203
1205 scene, view_layer, nullptr);
1206
1207 if (target == 2) {
1208 float center[2];
1209 if (!ED_uvedit_center_multi(scene, objects, center, sima->around)) {
1210 return OPERATOR_CANCELLED;
1211 }
1212 sub_v2_v2v2(offset, sima->cursor, center);
1213 }
1214
1215 bool changed_multi = false;
1216 for (Object *obedit : objects) {
1218
1219 if (synced_selection && (em->bm->totvertsel == 0)) {
1220 continue;
1221 }
1222
1223 bool changed = false;
1224 switch (target) {
1225 case 0:
1226 changed = uv_snap_uvs_to_pixels(sima, scene, obedit);
1227 break;
1228 case 1:
1229 changed = uv_snap_uvs_to_cursor(scene, obedit, sima->cursor);
1230 break;
1231 case 2:
1232 changed = uv_snap_uvs_offset(scene, obedit, offset);
1233 break;
1234 case 3:
1235 changed = uv_snap_uvs_to_adjacent_unselected(scene, obedit);
1236 break;
1237 }
1238
1239 if (changed) {
1240 changed_multi = true;
1241 uvedit_live_unwrap_update(sima, scene, obedit);
1242 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1243 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1244 }
1245 }
1246
1247 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1248}
1249
1251{
1252 static const EnumPropertyItem target_items[] = {
1253 {0, "PIXELS", 0, "Pixels", ""},
1254 {1, "CURSOR", 0, "Cursor", ""},
1255 {2, "CURSOR_OFFSET", 0, "Cursor (Offset)", ""},
1256 {3, "ADJACENT_UNSELECTED", 0, "Adjacent Unselected", ""},
1257 {0, nullptr, 0, nullptr, nullptr},
1258 };
1259
1260 /* identifiers */
1261 ot->name = "Snap Selection";
1262 ot->description = "Snap selected UV vertices to target type";
1263 ot->idname = "UV_OT_snap_selected";
1265
1266 /* api callbacks */
1269
1270 /* properties */
1272 ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1273}
1274
1277/* -------------------------------------------------------------------- */
1282{
1283 Scene *scene = CTX_data_scene(C);
1284 ViewLayer *view_layer = CTX_data_view_layer(C);
1285 BMFace *efa;
1286 BMLoop *l;
1287 BMIter iter, liter;
1288 const ToolSettings *ts = scene->toolsettings;
1289 const bool clear = RNA_boolean_get(op->ptr, "clear");
1290 const bool invert = RNA_boolean_get(op->ptr, "invert");
1291 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1292
1294 scene, view_layer, nullptr);
1295
1296 for (Object *obedit : objects) {
1298
1299 bool changed = false;
1300 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1301 BM_uv_map_ensure_pin_attr(em->bm, active_uv_name);
1302 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1303
1304 if (synced_selection && (em->bm->totvertsel == 0)) {
1305 continue;
1306 }
1307
1308 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1309 if (!uvedit_face_visible_test(scene, efa)) {
1310 continue;
1311 }
1312
1313 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1314
1315 if (uvedit_uv_select_test(scene, l, offsets)) {
1316 changed = true;
1317 if (invert) {
1318 BM_ELEM_CD_SET_BOOL(l, offsets.pin, !BM_ELEM_CD_GET_BOOL(l, offsets.pin));
1319 }
1320 else {
1321 BM_ELEM_CD_SET_BOOL(l, offsets.pin, !clear);
1322 }
1323 }
1324 }
1325 }
1326
1327 if (changed) {
1328 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1329 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SYNC_TO_EVAL);
1330 }
1331 }
1332
1333 return OPERATOR_FINISHED;
1334}
1335
1337{
1338 PropertyRNA *prop;
1339
1340 /* identifiers */
1341 ot->name = "Pin";
1342 ot->description =
1343 "Set/clear selected UV vertices as anchored between multiple unwrap operations";
1344 ot->idname = "UV_OT_pin";
1346
1347 /* api callbacks */
1348 ot->exec = uv_pin_exec;
1350
1351 /* properties */
1352 prop = RNA_def_boolean(
1353 ot->srna, "clear", false, "Clear", "Clear pinning for the selection instead of setting it");
1355 prop = RNA_def_boolean(ot->srna,
1356 "invert",
1357 false,
1358 "Invert",
1359 "Invert pinning for the selection instead of setting it");
1361}
1362
1365/* -------------------------------------------------------------------- */
1369/* check if we are selected or unselected based on 'bool_test' arg,
1370 * needed for select swap support */
1371#define UV_VERT_SEL_TEST(l, bool_test) (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) == bool_test)
1372
1373#define UV_EDGE_SEL_TEST(l, bool_test) (BM_ELEM_CD_GET_BOOL(l, offsets.select_edge) == bool_test)
1374
1375/* is every UV vert selected or unselected depending on bool_test */
1376static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, const BMUVOffsets offsets)
1377{
1378 BMLoop *l_iter;
1379 BMLoop *l_first;
1380
1381 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1382 do {
1383 if (!UV_EDGE_SEL_TEST(l_iter, select_test)) {
1384 return false;
1385 }
1386 } while ((l_iter = l_iter->next) != l_first);
1387
1388 return true;
1389}
1390
1392{
1393 ViewLayer *view_layer = CTX_data_view_layer(C);
1394 Scene *scene = CTX_data_scene(C);
1395 const ToolSettings *ts = scene->toolsettings;
1396 const bool swap = RNA_boolean_get(op->ptr, "unselected");
1397 const bool use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1398
1400 scene, view_layer, nullptr);
1401
1402 for (Object *ob : objects) {
1404 BMFace *efa;
1405 BMLoop *l;
1406 BMIter iter, liter;
1407
1408 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1409 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1410 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1411 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1412
1413 if (ts->uv_flag & UV_SYNC_SELECTION) {
1414 if (EDBM_mesh_hide(em, swap)) {
1415 Mesh *mesh = static_cast<Mesh *>(ob->data);
1417 params.calc_looptris = true;
1418 params.calc_normals = false;
1419 params.is_destructive = false;
1420 EDBM_update(mesh, &params);
1421 }
1422 continue;
1423 }
1424
1425 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1426 int hide = 0;
1427
1428 if (!uvedit_face_visible_test(scene, efa)) {
1429 continue;
1430 }
1431
1432 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1433
1434 if (UV_VERT_SEL_TEST(l, !swap) || UV_EDGE_SEL_TEST(l, !swap)) {
1435 hide = 1;
1436 break;
1437 }
1438 }
1439
1440 if (hide) {
1441 if (use_face_center) {
1442 if (em->selectmode == SCE_SELECT_FACE) {
1443 /* Deselect BMesh face if UV face is (de)selected depending on #swap. */
1444 if (bm_face_is_all_uv_sel(efa, !swap, offsets)) {
1445 BM_face_select_set(em->bm, efa, false);
1446 }
1447 uvedit_face_select_disable(scene, em->bm, efa, offsets);
1448 }
1449 else {
1450 if (bm_face_is_all_uv_sel(efa, true, offsets) == !swap) {
1451 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1452 /* For both cases rely on edge sel tests, since all vert sel tests are invalid in
1453 * case of sticky selections. */
1454 if (UV_EDGE_SEL_TEST(l, !swap) && (em->selectmode == SCE_SELECT_EDGE)) {
1455 BM_edge_select_set(em->bm, l->e, false);
1456 }
1457 else if (UV_EDGE_SEL_TEST(l, !swap) && (em->selectmode == SCE_SELECT_VERTEX)) {
1458 BM_vert_select_set(em->bm, l->v, false);
1459 }
1460 }
1461 }
1462 if (!swap) {
1463 uvedit_face_select_disable(scene, em->bm, efa, offsets);
1464 }
1465 }
1466 }
1467 else if (em->selectmode == SCE_SELECT_FACE) {
1468 /* Deselect BMesh face depending on the type of UV selectmode and the type of UV element
1469 * being considered. */
1470 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1471 if (UV_EDGE_SEL_TEST(l, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) {
1472 BM_face_select_set(em->bm, efa, false);
1473 break;
1474 }
1475 if (UV_VERT_SEL_TEST(l, !swap) && (ts->uv_selectmode == UV_SELECT_VERTEX)) {
1476 BM_face_select_set(em->bm, efa, false);
1477 break;
1478 }
1479 if (ts->uv_selectmode == UV_SELECT_ISLAND) {
1480 BM_face_select_set(em->bm, efa, false);
1481 break;
1482 }
1483 }
1484 uvedit_face_select_disable(scene, em->bm, efa, offsets);
1485 }
1486 else {
1487 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1488 if (UV_EDGE_SEL_TEST(l, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) {
1489 if (em->selectmode == SCE_SELECT_EDGE) {
1490 BM_edge_select_set(em->bm, l->e, false);
1491 }
1492 else {
1493 BM_vert_select_set(em->bm, l->v, false);
1494 BM_vert_select_set(em->bm, l->next->v, false);
1495 }
1496 }
1497 else if (UV_VERT_SEL_TEST(l, !swap) && (ts->uv_selectmode != UV_SELECT_EDGE)) {
1498 if (em->selectmode == SCE_SELECT_EDGE) {
1499 BM_edge_select_set(em->bm, l->e, false);
1500 }
1501 else {
1502 BM_vert_select_set(em->bm, l->v, false);
1503 }
1504 }
1505 }
1506 if (!swap) {
1507 uvedit_face_select_disable(scene, em->bm, efa, offsets);
1508 }
1509 }
1510 }
1511 }
1512
1513 /* Flush editmesh selections to ensure valid selection states. */
1514 if (em->selectmode != SCE_SELECT_FACE) {
1515 /* NOTE: Make sure correct flags are used. Previously this was done by passing
1516 * (SCE_SELECT_VERTEX | SCE_SELECT_EDGE), which doesn't work now that we support proper UV
1517 * edge selection. */
1518
1520 }
1521
1523
1524 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
1525 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
1526 }
1527
1528 return OPERATOR_FINISHED;
1529}
1530
1531#undef UV_VERT_SEL_TEST
1532#undef UV_EDGE_SEL_TEST
1533
1535{
1536 /* identifiers */
1537 ot->name = "Hide Selected";
1538 ot->description = "Hide (un)selected UV vertices";
1539 ot->idname = "UV_OT_hide";
1541
1542 /* api callbacks */
1543 ot->exec = uv_hide_exec;
1545
1546 /* props */
1548 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
1549}
1550
1553/* -------------------------------------------------------------------- */
1558{
1559 ViewLayer *view_layer = CTX_data_view_layer(C);
1560 Scene *scene = CTX_data_scene(C);
1561 const ToolSettings *ts = scene->toolsettings;
1562
1563 const bool use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1564 const bool select = RNA_boolean_get(op->ptr, "select");
1565
1567 scene, view_layer, nullptr);
1568
1569 for (Object *ob : objects) {
1571 BMFace *efa;
1572 BMLoop *l;
1573 BMIter iter, liter;
1574
1575 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1576 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1577 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1578 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1579
1580 /* NOTE: Selecting faces is delayed so that it doesn't select verts/edges and confuse certain
1581 * UV selection checks.
1582 * This creates a temporary state which breaks certain UV selection functions that do face
1583 * visibility checks internally. Current implementation handles each case separately. */
1584
1585 /* call the mesh function if we are in mesh sync sel */
1586 if (ts->uv_flag & UV_SYNC_SELECTION) {
1587 if (EDBM_mesh_reveal(em, select)) {
1588 Mesh *mesh = static_cast<Mesh *>(ob->data);
1590 params.calc_looptris = true;
1591 params.calc_normals = false;
1592 params.is_destructive = false;
1593 EDBM_update(mesh, &params);
1594 }
1595 continue;
1596 }
1597
1598 /* NOTE(@sidd017): Supporting selections in all cases is quite difficult considering there are
1599 * at least 12 cases to look into (3 mesh select-modes + 4 uv select-modes + sticky modes).
1600 * For now we select all UV faces as sticky disabled to ensure proper UV selection states (vert
1601 * + edge flags) */
1602 if (use_face_center) {
1603 if (em->selectmode == SCE_SELECT_FACE) {
1604 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1607 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1610 }
1611 // BM_face_select_set(em->bm, efa, true);
1613 }
1614 }
1615 }
1616 else {
1617 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1620 int totsel = 0;
1621 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1622 if (em->selectmode == SCE_SELECT_VERTEX) {
1623 totsel += BM_elem_flag_test(l->v, BM_ELEM_SELECT);
1624 }
1625 else if (em->selectmode == SCE_SELECT_EDGE) {
1626 totsel += BM_elem_flag_test(l->e, BM_ELEM_SELECT);
1627 }
1628 }
1629
1630 if (!totsel) {
1631 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1632
1635 }
1636 }
1637 // BM_face_select_set(em->bm, efa, true);
1639 }
1640 }
1641 }
1642 }
1643 else if (em->selectmode == SCE_SELECT_FACE) {
1644 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1647 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1650 }
1651 // BM_face_select_set(em->bm, efa, true);
1653 }
1654 }
1655 }
1656 else {
1657 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1660 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1663 }
1664 // BM_face_select_set(em->bm, efa, true);
1666 }
1667 }
1668 }
1669
1670 /* re-select tagged faces */
1672
1673 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
1674 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
1675 }
1676
1677 return OPERATOR_FINISHED;
1678}
1679
1681{
1682 /* identifiers */
1683 ot->name = "Reveal Hidden";
1684 ot->description = "Reveal all hidden UV vertices";
1685 ot->idname = "UV_OT_reveal";
1687
1688 /* api callbacks */
1691
1692 RNA_def_boolean(ot->srna, "select", true, "Select", "");
1693}
1694
1697/* -------------------------------------------------------------------- */
1702{
1703 SpaceImage *sima = CTX_wm_space_image(C);
1704
1705 if (!sima) {
1706 return OPERATOR_CANCELLED;
1707 }
1708
1709 RNA_float_get_array(op->ptr, "location", sima->cursor);
1710
1711 {
1712 wmMsgBus *mbus = CTX_wm_message_bus(C);
1713 bScreen *screen = CTX_wm_screen(C);
1714 WM_msg_publish_rna_prop(mbus, &screen->id, sima, SpaceImageEditor, cursor_location);
1715 }
1716
1718
1719 /* Use pass-through to allow click-drag to transform the cursor. */
1721}
1722
1723static int uv_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1724{
1725 ARegion *region = CTX_wm_region(C);
1726 float location[2];
1727
1728 if (region->regiontype == RGN_TYPE_WINDOW) {
1729 SpaceImage *sima = CTX_wm_space_image(C);
1730 if (sima && ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) {
1731 return OPERATOR_PASS_THROUGH;
1732 }
1733 }
1734
1736 &region->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
1737 RNA_float_set_array(op->ptr, "location", location);
1738
1739 return uv_set_2d_cursor_exec(C, op);
1740}
1741
1743{
1744 /* identifiers */
1745 ot->name = "Set 2D Cursor";
1746 ot->description = "Set 2D cursor location";
1747 ot->idname = "UV_OT_cursor_set";
1748
1749 /* api callbacks */
1753
1754 /* properties */
1756 "location",
1757 2,
1758 nullptr,
1759 -FLT_MAX,
1760 FLT_MAX,
1761 "Location",
1762 "Cursor location in normalized (0.0 to 1.0) coordinates",
1763 -10.0f,
1764 10.0f);
1765}
1766
1769/* -------------------------------------------------------------------- */
1774{
1775 Scene *scene = CTX_data_scene(C);
1776 ViewLayer *view_layer = CTX_data_view_layer(C);
1777 const bool mark_seams = RNA_boolean_get(op->ptr, "mark_seams");
1778 const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
1779 bool changed_multi = false;
1780
1782 scene, view_layer, nullptr);
1783
1784 for (Object *ob : objects) {
1785 Mesh *mesh = (Mesh *)ob->data;
1786 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1787 BMesh *bm = em->bm;
1788 BMIter iter;
1789
1790 if (!EDBM_uv_check(em)) {
1791 continue;
1792 }
1793
1794 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1795 bool changed = false;
1796
1797 BMFace *f;
1798 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1799 if (!uvedit_face_visible_test(scene, f)) {
1800 continue;
1801 }
1802
1803 BMLoop *l_iter;
1804 BMLoop *l_first;
1805
1806 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1807 do {
1808 if (l_iter == l_iter->radial_next) {
1809 continue;
1810 }
1811 if (!uvedit_edge_select_test(scene, l_iter, offsets)) {
1812 continue;
1813 }
1814
1815 bool mark = false;
1816 BMLoop *l_other = l_iter->radial_next;
1817 do {
1818 if (!BM_loop_uv_share_edge_check(l_iter, l_other, offsets.uv)) {
1819 mark = true;
1820 break;
1821 }
1822 } while ((l_other = l_other->radial_next) != l_iter);
1823
1824 if (mark) {
1825 if (mark_seams) {
1827 }
1828 if (mark_sharp) {
1830 }
1831 changed = true;
1832 }
1833 } while ((l_iter = l_iter->next) != l_first);
1834 }
1835
1836 if (changed) {
1837 changed_multi = true;
1838 DEG_id_tag_update(&mesh->id, 0);
1840 }
1841 }
1842
1843 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1844}
1845
1847{
1848 /* identifiers */
1849 ot->name = "Seams from Islands";
1850 ot->description = "Set mesh seams according to island setup in the UV editor";
1851 ot->idname = "UV_OT_seams_from_islands";
1852
1853 /* flags */
1855
1856 /* api callbacks */
1859
1860 RNA_def_boolean(ot->srna, "mark_seams", true, "Mark Seams", "Mark boundary edges as seams");
1861 RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark boundary edges as sharp");
1862}
1863
1866/* -------------------------------------------------------------------- */
1871{
1872 Scene *scene = CTX_data_scene(C);
1873 ViewLayer *view_layer = CTX_data_view_layer(C);
1874 const ToolSettings *ts = scene->toolsettings;
1875
1876 BMFace *efa;
1877 BMLoop *loop;
1878 BMIter iter, liter;
1879
1880 const bool flag_set = !RNA_boolean_get(op->ptr, "clear");
1881 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1882
1884 scene, view_layer, nullptr);
1885
1886 bool changed = false;
1887
1888 for (Object *ob : objects) {
1889 Mesh *mesh = (Mesh *)ob->data;
1890 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1891 BMesh *bm = em->bm;
1892
1893 if (synced_selection && (bm->totedgesel == 0)) {
1894 continue;
1895 }
1896
1897 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1898
1899 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1900 if (uvedit_face_visible_test(scene, efa)) {
1901 BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) {
1902 if (uvedit_edge_select_test(scene, loop, offsets)) {
1903 BM_elem_flag_set(loop->e, BM_ELEM_SEAM, flag_set);
1904 changed = true;
1905 }
1906 }
1907 }
1908 }
1909
1910 if (changed) {
1911 DEG_id_tag_update(&mesh->id, 0);
1913 }
1914 }
1915
1916 if (changed) {
1917 ED_uvedit_live_unwrap(scene, objects);
1918 }
1919
1920 return OPERATOR_FINISHED;
1921}
1922
1923static int uv_mark_seam_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
1924{
1925 uiPopupMenu *pup;
1926 uiLayout *layout;
1927
1928 if (RNA_struct_property_is_set(op->ptr, "clear")) {
1929 return uv_mark_seam_exec(C, op);
1930 }
1931
1932 pup = UI_popup_menu_begin(C, IFACE_("Edges"), ICON_NONE);
1933 layout = UI_popup_menu_layout(pup);
1934
1936 uiItemBooleanO(layout,
1938 ICON_NONE,
1939 op->type->idname,
1940 "clear",
1941 false);
1942 uiItemBooleanO(layout,
1944 ICON_NONE,
1945 op->type->idname,
1946 "clear",
1947 true);
1948
1949 UI_popup_menu_end(C, pup);
1950
1951 return OPERATOR_INTERFACE;
1952}
1953
1955{
1956 /* identifiers */
1957 ot->name = "Mark Seam";
1958 ot->description = "Mark selected UV edges as seams";
1959 ot->idname = "UV_OT_mark_seam";
1960
1961 /* flags */
1963
1964 /* api callbacks */
1968
1969 RNA_def_boolean(ot->srna, "clear", false, "Clear Seams", "Clear instead of marking seams");
1970}
1971
1974/* -------------------------------------------------------------------- */
1979{
1980 /* `uvedit_select.cc` */
1997
2000
2002
2007
2013
2024
2029
2031}
2032
2034{
2036 wmOperatorTypeMacro *otmacro;
2037
2038 ot = WM_operatortype_append_macro("UV_OT_rip_move",
2039 "UV Rip Move",
2040 "Unstitch UVs and move the result",
2042 WM_operatortype_macro_define(ot, "UV_OT_rip");
2043 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
2044 RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
2045 RNA_boolean_set(otmacro->ptr, "mirror", false);
2046}
2047
2049{
2050 wmKeyMap *keymap;
2051
2052 keymap = WM_keymap_ensure(keyconf, "UV Editor", SPACE_EMPTY, RGN_TYPE_WINDOW);
2053 keymap->poll = ED_operator_uvedit;
2054}
2055
SpaceImage * CTX_wm_space_image(const bContext *C)
bScreen * CTX_wm_screen(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmMsgBus * CTX_wm_message_bus(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
struct Material * BKE_object_material_get_eval(struct Object *ob, short act)
#define SH_NODE_TEX_IMAGE
Definition BKE_node.hh:930
#define SH_NODE_TEX_ENVIRONMENT
Definition BKE_node.hh:940
#define BLI_assert(a)
Definition BLI_assert.h:50
A KD-tree for nearest neighbor search.
float closest_to_line_segment_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:363
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
void mid_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v2(float r[2])
MINLINE void mul_v2_v2fl(float r[2], const float a[2], float f)
unsigned int uint
#define INIT_MINMAX2(min, max)
#define UNUSED_FUNCTION(x)
#define ENUM_OPERATORS(_type, _max)
#define ELEM(...)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
bool DEG_is_evaluated_object(const Object *object)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
@ OB_MESH
@ UV_SELECT_VERTEX
@ UV_SELECT_FACE
@ UV_SELECT_EDGE
@ UV_SELECT_ISLAND
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ UV_SYNC_SELECTION
@ RGN_TYPE_WINDOW
@ SI_LIVE_UNWRAP
@ SPACE_EMPTY
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ OPERATOR_PASS_THROUGH
void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
bool ED_space_image_cursor_poll(bContext *C)
bool ED_space_image_show_cache_and_mval_over(const SpaceImage *sima, ARegion *region, const int mval[2])
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
bool EDBM_mesh_reveal(BMEditMesh *em, bool select)
bool EDBM_uv_check(BMEditMesh *em)
void BM_uv_element_map_free(UvElementMap *element_map)
bool EDBM_mesh_hide(BMEditMesh *em, bool swap)
UvElementMap * BM_uv_element_map_create(BMesh *bm, const Scene *scene, bool uv_selected, bool use_winding, bool use_seams, bool do_islands)
void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *ntree)
Definition node_edit.cc:492
bool ED_operator_uvedit_space_image(bContext *C)
bool ED_operator_uvedit(bContext *C)
void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit, struct wmWindow *win_modal)
bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, BMUVOffsets offsets)
void ED_uvedit_live_unwrap_re_solve()
bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
void uvedit_face_select_disable(const Scene *scene, BMesh *bm, BMFace *efa, BMUVOffsets offsets)
void ED_uvedit_live_unwrap_end(bool cancel)
void ED_uvedit_live_unwrap(const Scene *scene, blender::Span< Object * > objects)
bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, BMUVOffsets offsets)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
#define C
Definition RandGen.cpp:29
void uiItemBooleanO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define ND_SPACE_IMAGE
Definition WM_types.hh:488
#define ND_SELECT
Definition WM_types.hh:474
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
#define NC_SPACE
Definition WM_types.hh:359
#define BM_ELEM_CD_GET_BOOL(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#define BM_ELEM_CD_SET_BOOL(ele, offset, f)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_uv_map_ensure_pin_attr(BMesh *bm, const char *uv_map_name)
void BM_uv_map_ensure_vert_select_attr(BMesh *bm, const char *uv_map_name)
void BM_uv_map_ensure_edge_select_attr(BMesh *bm, const char *uv_map_name)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_FACE
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_select_mode_flush(BMesh *bm)
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_select_history_validate(BMesh *bm)
void BM_mesh_elem_hflag_enable_test(BMesh *bm, const char htype, const char hflag, const bool respecthide, const bool overwrite, const char hflag_test)
#define BM_FACE
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
BMUVOffsets BM_uv_map_get_offsets(const BMesh *bm)
bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
int64_t size() const
void append(const T &value)
void resize(const int64_t new_size)
void reserve(const int64_t min_capacity)
OperationNode * node
int len
KDTree_3d * tree
draw_view in_light_buf[] float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition invert.h:9
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static void clear(Message &msg)
Definition msgfmt.cc:218
bNode * node_get_active_texture(bNodeTree *ntree)
T square(const T &a)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< float, 2 > float2
static void update(bNodeTree *ntree)
return ret
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
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)
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_flag(PropertyRNA *prop, PropertyFlag flag)
#define min(a, b)
Definition sort.c:32
#define FLT_MAX
Definition stdcycles.h:14
short selectmode
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
int totvert
int totvertsel
int totloop
int totedgesel
CustomData ldata
Definition DNA_ID.h:413
struct bNodeTree * nodetree
UvElement * storage
struct ID * id
int mval[2]
Definition WM_types.hh:728
bool(* poll)(struct bContext *)
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct wmOperatorType * type
struct PointerRNA * ptr
void UV_OT_copy(wmOperatorType *ot)
void UV_OT_paste(wmOperatorType *ot)
void UV_OT_select_all(wmOperatorType *ot)
void UV_OT_select_edge_ring(wmOperatorType *ot)
void UV_OT_select(wmOperatorType *ot)
void UV_OT_select_split(wmOperatorType *ot)
void UV_OT_shortest_path_pick(wmOperatorType *ot)
void UV_OT_select_linked(wmOperatorType *ot)
void UV_OT_stitch(wmOperatorType *ot)
bool uvedit_select_is_any_selected_multi(const Scene *scene, blender::Span< Object * > objects)
void UV_OT_cylinder_project(wmOperatorType *ot)
void UV_OT_project_from_view(wmOperatorType *ot)
void UV_OT_smart_project(wmOperatorType *ot)
void UV_OT_unwrap(wmOperatorType *ot)
void UV_OT_select_circle(wmOperatorType *ot)
void UV_OT_select_mode(wmOperatorType *ot)
void UV_OT_select_similar(wmOperatorType *ot)
void UV_OT_sphere_project(wmOperatorType *ot)
void UV_OT_rip(wmOperatorType *ot)
void UV_OT_select_linked_pick(wmOperatorType *ot)
void UV_OT_select_more(wmOperatorType *ot)
void UV_OT_cube_project(wmOperatorType *ot)
void UV_OT_select_pinned(wmOperatorType *ot)
void UV_OT_select_loop(wmOperatorType *ot)
void UV_OT_shortest_path_select(wmOperatorType *ot)
void UV_OT_average_islands_scale(wmOperatorType *ot)
void UV_OT_reset(wmOperatorType *ot)
void UV_OT_select_overlap(wmOperatorType *ot)
void UV_OT_minimize_stretch(wmOperatorType *ot)
void UV_OT_select_lasso(wmOperatorType *ot)
void UV_OT_pack_islands(wmOperatorType *ot)
void UV_OT_select_less(wmOperatorType *ot)
void UV_OT_select_box(wmOperatorType *ot)
bool ED_object_get_active_image(Object *ob, int mat_nr, Image **r_ima, ImageUser **r_iuser, const bNode **r_node, const bNodeTree **r_ntree)
void ED_keymap_uvedit(wmKeyConfig *keyconf)
static void UV_OT_snap_cursor(wmOperatorType *ot)
static void UV_OT_cursor_set(wmOperatorType *ot)
eUVEndPointPrecedence
@ UVEP_PINNED
@ UVEP_SELECTED
@ UVEP_INVALID
static int uv_snap_selection_exec(bContext *C, wmOperator *op)
void ED_operatortypes_uvedit()
static int uv_remove_doubles_to_selected(bContext *C, wmOperator *op)
static int uv_remove_doubles_exec(bContext *C, wmOperator *op)
static void UV_OT_align(wmOperatorType *ot)
static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool)
static int uv_hide_exec(bContext *C, wmOperator *op)
static void uv_snap_cursor_to_origin(float uvco[2])
static void UV_OT_mark_seam(wmOperatorType *ot)
static void UV_OT_weld(wmOperatorType *ot)
static bool uv_snap_uvs_to_pixels(SpaceImage *sima, Scene *scene, Object *obedit)
static bool uv_snap_uvs_to_cursor(Scene *scene, Object *obedit, const float cursor[2])
bool ED_uvedit_test(Object *obedit)
Definition uvedit_ops.cc:69
static void uv_weld_align(bContext *C, eUVWeldAlign tool)
static bool uvedit_uv_align_weld(Scene *scene, BMesh *bm, const eUVWeldAlign tool, const float cent[2])
void ED_uvedit_select_all(BMesh *bm)
static void UV_OT_snap_selected(wmOperatorType *ot)
static void uv_snap_to_pixel(float uvco[2], float w, float h)
void ED_uvedit_foreach_uv_multi(const Scene *scene, const Span< Object * > objects_edit, const bool skip_invisible, const bool skip_nonselected, FunctionRef< void(float[2])> user_fn)
static bool uv_snap_uvs_to_adjacent_unselected(Scene *scene, Object *obedit)
bool ED_uvedit_center_from_pivot_ex(SpaceImage *sima, Scene *scene, ViewLayer *view_layer, float r_center[2], char mode, bool *r_has_select)
static int uv_snap_cursor_exec(bContext *C, wmOperator *op)
static eUVEndPointPrecedence uvedit_line_update_get_precedence(const bool pinned)
bool ED_uvedit_center_multi(const Scene *scene, Span< Object * > objects_edit, float cent[2], char mode)
static void UV_OT_reveal(wmOperatorType *ot)
static int uv_mark_seam_invoke(bContext *C, wmOperator *op, const wmEvent *)
static bool uv_snap_uvs_offset(Scene *scene, Object *obedit, const float offset[2])
void ED_uvedit_foreach_uv(const Scene *scene, BMesh *bm, const bool skip_invisible, const bool selected, FunctionRef< void(float[2])> user_fn)
eUVWeldAlign
@ UV_ALIGN_Y
@ UV_STRAIGHTEN
@ UV_WELD
@ UV_STRAIGHTEN_Y
@ UV_ALIGN_AUTO
@ UV_ALIGN_X
@ UV_STRAIGHTEN_X
static bool uv_snap_cursor_to_selection(Scene *scene, Span< Object * > objects_edit, SpaceImage *sima)
#define UV_EDGE_SEL_TEST(l, bool_test)
static int uv_align_exec(bContext *C, wmOperator *op)
static void UV_OT_hide(wmOperatorType *ot)
static int uv_seams_from_islands_exec(bContext *C, wmOperator *op)
static void uv_snap_cursor_to_pixels(SpaceImage *sima)
static bool uvedit_line_update_endpoint(const float *luv, const bool pinned, float uv_a[2], eUVEndPointPrecedence *prec_a, float uv_b[2], eUVEndPointPrecedence *prec_b)
static int uv_pin_exec(bContext *C, wmOperator *op)
void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
bool ED_uvedit_minmax_multi(const Scene *scene, const Span< Object * > objects_edit, float r_min[2], float r_max[2])
static void UV_OT_seams_from_islands(wmOperatorType *ot)
static int uv_weld_exec(bContext *C, wmOperator *)
void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima)
static int UNUSED_FUNCTION ED_operator_uvmap_mesh(bContext *C)
Definition uvedit_ops.cc:88
#define UV_VERT_SEL_TEST(l, bool_test)
static int uv_remove_doubles_to_selected_shared_vertex(bContext *C, wmOperator *op)
static void UV_OT_pin(wmOperatorType *ot)
static int uv_reveal_exec(bContext *C, wmOperator *op)
void ED_operatormacros_uvedit()
static bool ED_uvedit_median_multi(const Scene *scene, const Span< Object * > objects_edit, float co[2])
static int uv_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int uv_mark_seam_exec(bContext *C, wmOperator *op)
static int uv_remove_doubles_to_unselected(bContext *C, wmOperator *op)
static void UV_OT_remove_doubles(wmOperatorType *ot)
static bool is_image_texture_node(bNode *node)
static bool uvedit_uv_straighten_elements(const UvElement *element, const int len, const BMUVOffsets offsets, const eUVWeldAlign tool)
static int uv_set_2d_cursor_exec(bContext *C, wmOperator *op)
static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, const BMUVOffsets offsets)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:897
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)
wmOperatorTypeMacro * WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorType * WM_operatortype_append_macro(const char *idname, const char *name, const char *description, int flag)