Blender V4.5
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
8
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"
37#include "BKE_material.hh"
38#include "BKE_mesh_mapping.hh"
39#include "BKE_mesh_types.hh"
40#include "BKE_node.hh"
42
43#include "DEG_depsgraph.hh"
45
46#include "ED_image.hh"
47#include "ED_mesh.hh"
48#include "ED_node.hh"
49#include "ED_screen.hh"
50#include "ED_uvedit.hh"
51
52#include "RNA_access.hh"
53#include "RNA_define.hh"
54
55#include "WM_api.hh"
56#include "WM_message.hh"
57#include "WM_types.hh"
58
59#include "UI_interface.hh"
60#include "UI_resources.hh"
61#include "UI_view2d.hh"
62
63#include "uvedit_intern.hh"
64
65using namespace blender;
66
67/* -------------------------------------------------------------------- */
70
72{
73 BMEditMesh *em;
74 int ret;
75
76 if (!obedit) {
77 return false;
78 }
79
80 if (obedit->type != OB_MESH) {
81 return false;
82 }
83
84 em = BKE_editmesh_from_object(obedit);
85 ret = EDBM_uv_check(em);
86
87 return ret;
88}
89
91{
93
94 if (ob && ob->type == OB_MESH) {
95 Mesh *mesh = static_cast<Mesh *>(ob->data);
96
97 if (CustomData_get_layer(&mesh->corner_data, CD_PROP_FLOAT2) != nullptr) {
98 return 1;
99 }
100 }
101
102 return 0;
103}
104
106
107/* -------------------------------------------------------------------- */
110
115
117 int mat_nr,
118 Image **r_ima,
119 ImageUser **r_iuser,
120 const bNode **r_node,
121 const bNodeTree **r_ntree)
122{
124 BKE_object_material_get(ob, mat_nr);
125 bNodeTree *ntree = (ma && ma->use_nodes) ? ma->nodetree : nullptr;
126 bNode *node = (ntree) ? bke::node_get_active_texture(*ntree) : nullptr;
127
128 if (node && is_image_texture_node(node)) {
129 if (r_ima) {
130 *r_ima = (Image *)node->id;
131 }
132 if (r_iuser) {
133 if (node->type_legacy == SH_NODE_TEX_IMAGE) {
134 *r_iuser = &((NodeTexImage *)node->storage)->iuser;
135 }
136 else if (node->type_legacy == SH_NODE_TEX_ENVIRONMENT) {
137 *r_iuser = &((NodeTexEnvironment *)node->storage)->iuser;
138 }
139 else {
140 *r_iuser = nullptr;
141 }
142 }
143 if (r_node) {
144 *r_node = node;
145 }
146 if (r_ntree) {
147 *r_ntree = ntree;
148 }
149 return true;
150 }
151
152 if (r_ima) {
153 *r_ima = nullptr;
154 }
155 if (r_iuser) {
156 *r_iuser = nullptr;
157 }
158 if (r_node) {
159 *r_node = node;
160 }
161 if (r_ntree) {
162 *r_ntree = ntree;
163 }
164
165 return false;
166}
167
168void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima)
169{
170 Material *ma = BKE_object_material_get(ob, mat_nr);
171 bNode *node = (ma && ma->use_nodes) ? bke::node_get_active_texture(*ma->nodetree) : nullptr;
172
173 if (node && is_image_texture_node(node)) {
174 node->id = &ima->id;
176 }
177}
178
180
181/* -------------------------------------------------------------------- */
184
186{
187 if (sima && (sima->flag & SI_LIVE_UNWRAP)) {
188 ED_uvedit_live_unwrap_begin(scene, obedit, nullptr);
191 }
192}
193
195
196/* -------------------------------------------------------------------- */
199
200void ED_uvedit_foreach_uv(const Scene *scene,
201 BMesh *bm,
202 const bool skip_invisible,
203 const bool selected,
204 FunctionRef<void(float[2])> user_fn)
205{
206 /* Check selection for quick return. */
207 const bool synced_selection = (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
208 if (synced_selection && bm->totvertsel == (selected ? 0 : bm->totvert)) {
209 return;
210 }
211
212 BMFace *efa;
213 BMLoop *l;
214 BMIter iter, liter;
215
216 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
217
218 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
219 if (skip_invisible && !uvedit_face_visible_test(scene, efa)) {
220 continue;
221 }
222
223 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
224 if (uvedit_uv_select_test(scene, l, offsets) == selected) {
225 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
226 user_fn(luv);
227 }
228 }
229 }
230}
231
233 const Span<Object *> objects_edit,
234 const bool skip_invisible,
235 const bool skip_nonselected,
236 FunctionRef<void(float[2])> user_fn)
237{
238 for (Object *obedit : objects_edit) {
240 ED_uvedit_foreach_uv(scene, em->bm, skip_invisible, skip_nonselected, user_fn);
241 }
242}
243
245 const Span<Object *> objects_edit,
246 float r_min[2],
247 float r_max[2])
248{
249 bool changed = false;
250 INIT_MINMAX2(r_min, r_max);
251 ED_uvedit_foreach_uv_multi(scene, objects_edit, true, true, [&](float luv[2]) {
252 minmax_v2v2_v2(r_min, r_max, luv);
253 changed = true;
254 });
255 return changed;
256}
257
259{
260 BMFace *efa;
261 BMLoop *l;
262 BMIter iter, liter;
263 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
264
265 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
266 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
267 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
268 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
269 }
270 }
271}
272
273static bool uvedit_median_multi(const Scene *scene, const Span<Object *> objects_edit, 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 (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
346
347/* -------------------------------------------------------------------- */
350
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
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_offsets_get(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 float cent[2], min[2], max[2];
545
547
549 scene, view_layer, nullptr);
550
551 if (tool == UV_ALIGN_AUTO) {
553 scene, objects, true, true, [&](float luv[2]) { minmax_v2v2_v2(min, max, luv); });
554 tool = (max[0] - min[0] >= max[1] - min[1]) ? UV_ALIGN_Y : UV_ALIGN_X;
555 }
556
557 ED_uvedit_center_multi(scene, objects, cent, 0);
558
559 for (Object *obedit : objects) {
561 bool changed = false;
562
563 if (em->bm->totvertsel == 0) {
564 continue;
565 }
566
568 changed |= uvedit_uv_align_weld(scene, em->bm, tool, cent);
569 }
570
572 changed |= uvedit_uv_straighten(scene, em->bm, tool);
573 }
574
575 if (changed) {
576 uvedit_live_unwrap_update(sima, scene, obedit);
577 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
578 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
579 }
580 }
581}
582
589
591{
592 static const EnumPropertyItem axis_items[] = {
594 "ALIGN_S",
595 0,
596 "Straighten",
597 "Align UV vertices along the line defined by the endpoints"},
599 "ALIGN_T",
600 0,
601 "Straighten X",
602 "Align UV vertices, moving them horizontally to the line defined by the endpoints"},
604 "ALIGN_U",
605 0,
606 "Straighten Y",
607 "Align UV vertices, moving them vertically to the line defined by the endpoints"},
609 "ALIGN_AUTO",
610 0,
611 "Align Auto",
612 "Automatically choose the direction on which there is most alignment already"},
613 {UV_ALIGN_X, "ALIGN_X", 0, "Align Vertically", "Align UV vertices on a vertical line"},
614 {UV_ALIGN_Y, "ALIGN_Y", 0, "Align Horizontally", "Align UV vertices on a horizontal line"},
615 {0, nullptr, 0, nullptr, nullptr},
616 };
617
618 /* identifiers */
619 ot->name = "Align";
620 ot->description = "Aligns selected UV vertices on a line";
621 ot->idname = "UV_OT_align";
623
624 /* API callbacks. */
625 ot->exec = uv_align_exec;
626 ot->poll = ED_operator_uvedit;
627
628 /* properties */
630 ot->srna, "axis", axis_items, UV_ALIGN_AUTO, "Axis", "Axis to align UV locations on");
631}
632
634
635/* -------------------------------------------------------------------- */
638
640{
641 Scene *scene = CTX_data_scene(C);
642 ViewLayer *view_layer = CTX_data_view_layer(C);
644
645 const float threshold = RNA_float_get(op->ptr, "threshold");
646
648 scene, view_layer, nullptr);
649
650 bool *changed = MEM_calloc_arrayN<bool>(objects.size(), __func__);
651
652 /* Maximum index of an objects[i]'s UVs in UV_arr.
653 * It helps find which UV in *mloopuv_arr belongs to which object. */
654 uint *ob_mloopuv_max_idx = MEM_calloc_arrayN<uint>(objects.size(), __func__);
655
656 /* Calculate max possible number of kdtree nodes. */
657 int uv_maxlen = 0;
658 for (Object *obedit : objects) {
660
661 if (em->bm->totvertsel == 0) {
662 continue;
663 }
664
665 uv_maxlen += em->bm->totloop;
666 }
667
668 KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
669
670 blender::Vector<int> duplicates;
671 blender::Vector<float *> mloopuv_arr;
672
673 int mloopuv_count = 0; /* Also used for *duplicates count. */
674
675 for (const int ob_index : objects.index_range()) {
676 Object *obedit = objects[ob_index];
678 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
679 BLI_kdtree_2d_insert(tree, mloopuv_count, luv);
680 duplicates.append(-1);
681 mloopuv_arr.append(luv);
682 mloopuv_count++;
683 });
684
685 ob_mloopuv_max_idx[ob_index] = mloopuv_count - 1;
686 }
687
688 BLI_kdtree_2d_balance(tree);
689 int found_duplicates = BLI_kdtree_2d_calc_duplicates_fast(
690 tree, threshold, false, duplicates.data());
691
692 if (found_duplicates > 0) {
693 /* Calculate average uv for duplicates. */
694 int *uv_duplicate_count = MEM_calloc_arrayN<int>(mloopuv_count, __func__);
695 for (int i = 0; i < mloopuv_count; i++) {
696 if (duplicates[i] == -1) { /* If doesn't reference another */
697 uv_duplicate_count[i]++; /* self */
698 continue;
699 }
700
701 if (duplicates[i] != i) {
702 /* If not self then accumulate uv for averaging.
703 * Self uv is already present in accumulator */
704 add_v2_v2(mloopuv_arr[duplicates[i]], mloopuv_arr[i]);
705 }
706 uv_duplicate_count[duplicates[i]]++;
707 }
708
709 for (int i = 0; i < mloopuv_count; i++) {
710 if (uv_duplicate_count[i] < 2) {
711 continue;
712 }
713
714 mul_v2_fl(mloopuv_arr[i], 1.0f / float(uv_duplicate_count[i]));
715 }
716 MEM_freeN(uv_duplicate_count);
717
718 /* Update duplicated uvs. */
719 uint ob_index = 0;
720 for (int i = 0; i < mloopuv_count; i++) {
721 /* Make sure we know which object owns the mloopuv at this index.
722 * Remember that in some cases the object will have no loop uv,
723 * thus we need the while loop, and not simply an if check. */
724 while (ob_mloopuv_max_idx[ob_index] < i) {
725 ob_index++;
726 }
727
728 if (duplicates[i] == -1) {
729 continue;
730 }
731
732 copy_v2_v2(mloopuv_arr[i], mloopuv_arr[duplicates[i]]);
733 changed[ob_index] = true;
734 }
735
736 for (ob_index = 0; ob_index < objects.size(); ob_index++) {
737 if (changed[ob_index]) {
738 Object *obedit = objects[ob_index];
739 uvedit_live_unwrap_update(sima, scene, obedit);
740 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
742 }
743 }
744 }
745
746 BLI_kdtree_2d_free(tree);
747 MEM_freeN(changed);
748 MEM_freeN(ob_mloopuv_max_idx);
749
750 return OPERATOR_FINISHED;
751}
752
754{
755 Scene *scene = CTX_data_scene(C);
756 ViewLayer *view_layer = CTX_data_view_layer(C);
758 const float threshold = RNA_float_get(op->ptr, "threshold");
759
761 scene, view_layer, nullptr);
762
763 /* Calculate max possible number of kdtree nodes. */
764 int uv_maxlen = 0;
765 for (Object *obedit : objects) {
767 uv_maxlen += em->bm->totloop;
768 }
769
770 KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
771
772 blender::Vector<float *> mloopuv_arr;
773
774 int mloopuv_count = 0;
775
776 /* Add visible non-selected uvs to tree */
777 ED_uvedit_foreach_uv_multi(scene, objects, true, false, [&](float luv[2]) {
778 BLI_kdtree_2d_insert(tree, mloopuv_count, luv);
779 mloopuv_arr.append(luv);
780 mloopuv_count++;
781 });
782
783 BLI_kdtree_2d_balance(tree);
784
785 /* For each selected uv, find duplicate non selected uv. */
786 for (Object *obedit : objects) {
787 bool changed = false;
789 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
790 KDTreeNearest_2d nearest;
791 const int i = BLI_kdtree_2d_find_nearest(tree, luv, &nearest);
792
793 if (i != -1 && nearest.dist < threshold) {
794 copy_v2_v2(luv, mloopuv_arr[i]);
795 changed = true;
796 }
797 });
798
799 if (changed) {
800 uvedit_live_unwrap_update(sima, scene, obedit);
801 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
802 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
803 }
804 }
805
806 BLI_kdtree_2d_free(tree);
807
808 return OPERATOR_FINISHED;
809}
810
812{
813 /* NOTE: The calculation for the center-point of loops belonging to a vertex will be skewed
814 * if one UV coordinate holds more loops than the others. */
815
816 Scene *scene = CTX_data_scene(C);
818 ViewLayer *view_layer = CTX_data_view_layer(C);
820 scene, view_layer, nullptr);
821
822 /* Only use the squared distance, to avoid a square-root. */
823 const float threshold_sq = math::square(RNA_float_get(op->ptr, "threshold"));
824
825 for (Object *obedit : objects) {
827 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
828 BMVert *v;
829 BMLoop *l;
830 BMIter viter, liter;
831
832 /* The `changed` variable keeps track if any loops from the current object are merged. */
834 uvs.reserve(32);
835 bool changed = false;
836
837 BM_ITER_MESH (v, &viter, em->bm, BM_VERTS_OF_MESH) {
838
839 BLI_assert(uvs.size() == 0);
840 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
841 if (uvedit_uv_select_test(scene, l, offsets)) {
842 uvs.append(BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv));
843 }
844 }
845 if (uvs.size() <= 1) {
846 uvs.clear();
847 continue;
848 }
849
850 while (uvs.size() > 1) {
851 const int uvs_num = uvs.size();
852 float2 uv_average = {0.0f, 0.0f};
853 for (const float *luv : uvs) {
854 uv_average += float2(luv);
855 }
856 uv_average /= uvs_num;
857
858 /* Find the loop closest to the uv_average. This loop will be the base that all
859 * other loop's distances are calculated from. */
860
861 float dist_best_sq = math::distance_squared(uv_average, float2(uvs[0]));
862 float *uv_ref = uvs[0];
863 int uv_ref_index = 0;
864 for (int i = 1; i < uvs_num; i++) {
865 const float dist_test_sq = math::distance_squared(uv_average, float2(uvs[i]));
866 if (dist_test_sq < dist_best_sq) {
867 dist_best_sq = dist_test_sq;
868 uv_ref = uvs[i];
869 uv_ref_index = i;
870 }
871 }
872
873 const int uvs_end = uvs_num - 1;
874 std::swap(uvs[uv_ref_index], uvs[uvs_end]);
875
876 /* Move all the UVs within threshold to the end of the array. Sum of all UV coordinates
877 * within threshold is initialized with `uv_ref` coordinate data since while loop
878 * ends once it hits `uv_ref` UV. */
879 float2 uv_merged_average = {uv_ref[0], uv_ref[1]};
880 int i = 0;
881 int uvs_num_merged = 1;
882 while (uvs[i] != uv_ref && i < uvs_num - uvs_num_merged) {
883 const float dist_test_sq = len_squared_v2v2(uv_ref, uvs[i]);
884 if (dist_test_sq < threshold_sq) {
885 uv_merged_average += float2(uvs[i]);
886 std::swap(uvs[i], uvs[uvs_end - uvs_num_merged]);
887 uvs_num_merged++;
888 if (dist_test_sq != 0.0f) {
889 changed = true;
890 }
891 }
892 else {
893 i++;
894 }
895 }
896
897 /* Recalculate `uv_average` so it only considers UV's that are being included in merge
898 * operation. Then Shift all loops to that position. */
899 if (uvs_num_merged > 1) {
900 uv_merged_average /= uvs_num_merged;
901
902 for (int j = uvs_num - uvs_num_merged; j < uvs_num; j++) {
903 copy_v2_v2(uvs[j], uv_merged_average);
904 }
905 }
906
907 uvs.resize(uvs_num - uvs_num_merged);
908 }
909 uvs.clear();
910 }
911 if (changed) {
912 uvedit_live_unwrap_update(sima, scene, obedit);
913 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
914 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
915 }
916 }
917
918 return OPERATOR_FINISHED;
919}
920
922{
923 if (RNA_boolean_get(op->ptr, "use_unselected")) {
925 }
926 if (RNA_boolean_get(op->ptr, "use_shared_vertex")) {
928 }
930}
931
933{
934 /* identifiers */
935 ot->name = "Merge UVs by Distance";
936 ot->description =
937 "Selected UV vertices that are within a radius of each other are welded together";
938 ot->idname = "UV_OT_remove_doubles";
940
941 /* API callbacks. */
943 ot->poll = ED_operator_uvedit;
944
945 RNA_def_float(ot->srna,
946 "threshold",
947 0.02f,
948 0.0f,
949 10.0f,
950 "Merge Distance",
951 "Maximum distance between welded vertices",
952 0.0f,
953 1.0f);
954 RNA_def_boolean(ot->srna,
955 "use_unselected",
956 false,
957 "Unselected",
958 "Merge selected to other unselected vertices");
960 ot->srna, "use_shared_vertex", false, "Shared Vertex", "Weld UVs based on shared vertices");
961}
962
964
965/* -------------------------------------------------------------------- */
968
970{
972
973 return OPERATOR_FINISHED;
974}
975
977{
978 /* identifiers */
979 ot->name = "Weld";
980 ot->description = "Weld selected UV vertices together";
981 ot->idname = "UV_OT_weld";
983
984 /* API callbacks. */
985 ot->exec = uv_weld_exec;
986 ot->poll = ED_operator_uvedit;
987}
988
990
991/* -------------------------------------------------------------------- */
994
995static void uv_snap_to_pixel(float uvco[2], float w, float h)
996{
997 uvco[0] = roundf(uvco[0] * w) / w;
998 uvco[1] = roundf(uvco[1] * h) / h;
999}
1000
1002{
1003 int width = 0, height = 0;
1004
1005 ED_space_image_get_size(sima, &width, &height);
1006 uv_snap_to_pixel(sima->cursor, width, height);
1007}
1008
1010 Span<Object *> objects_edit,
1011 SpaceImage *sima)
1012{
1013 return ED_uvedit_center_multi(scene, objects_edit, sima->cursor, sima->around);
1014}
1015
1016static void uv_snap_cursor_to_origin(float uvco[2])
1017{
1018 uvco[0] = 0;
1019 uvco[1] = 0;
1020}
1021
1023{
1025
1026 bool changed = false;
1027
1028 switch (RNA_enum_get(op->ptr, "target")) {
1029 case 0:
1031 changed = true;
1032 break;
1033 case 1: {
1034 Scene *scene = CTX_data_scene(C);
1035 ViewLayer *view_layer = CTX_data_view_layer(C);
1036
1037 Vector<Object *> objects =
1039 scene, view_layer, nullptr);
1040 changed = uv_snap_cursor_to_selection(scene, objects, sima);
1041 break;
1042 }
1043 case 2:
1045 changed = true;
1046 break;
1047 }
1048
1049 if (!changed) {
1050 return OPERATOR_CANCELLED;
1051 }
1052
1054
1055 return OPERATOR_FINISHED;
1056}
1057
1059{
1060 static const EnumPropertyItem target_items[] = {
1061 {0, "PIXELS", 0, "Pixels", ""},
1062 {1, "SELECTED", 0, "Selected", ""},
1063 {2, "ORIGIN", 0, "Origin", ""},
1064 {0, nullptr, 0, nullptr, nullptr},
1065 };
1066
1067 /* identifiers */
1068 ot->name = "Snap Cursor";
1069 ot->description = "Snap cursor to target type";
1070 ot->idname = "UV_OT_snap_cursor";
1071 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1072
1073 /* API callbacks. */
1074 ot->exec = uv_snap_cursor_exec;
1075 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
1076
1077 /* properties */
1079 ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1080}
1081
1083
1084/* -------------------------------------------------------------------- */
1087
1088static bool uv_snap_uvs_to_cursor(Scene *scene, Object *obedit, const float cursor[2])
1089{
1091 bool changed = false;
1092
1093 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1094 copy_v2_v2(luv, cursor);
1095 changed = true;
1096 });
1097
1098 return changed;
1099}
1100
1101static bool uv_snap_uvs_offset(Scene *scene, Object *obedit, const float offset[2])
1102{
1104 bool changed = false;
1105
1106 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1107 add_v2_v2(luv, offset);
1108 changed = true;
1109 });
1110
1111 return changed;
1112}
1113
1115{
1117 BMesh *bm = em->bm;
1118 BMFace *f;
1119 BMLoop *l, *lsub;
1120 BMIter iter, liter, lsubiter;
1121 float *luv;
1122 bool changed = false;
1123 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1124
1125 /* Index every vert that has a selected UV using it, but only once so as to
1126 * get unique indices and to count how much to `malloc`. */
1127 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1128 if (uvedit_face_visible_test(scene, f)) {
1130 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1132 }
1133 }
1134 else {
1136 }
1137 }
1138
1139 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1140 if (BM_elem_flag_test(f, BM_ELEM_TAG)) { /* Face: visible. */
1141 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1142 if (BM_elem_flag_test(l, BM_ELEM_TAG)) { /* Loop: selected. */
1143 float uv[2] = {0.0f, 0.0f};
1144 int uv_tot = 0;
1145
1146 BM_ITER_ELEM (lsub, &lsubiter, l->v, BM_LOOPS_OF_VERT) {
1147 if (BM_elem_flag_test(lsub->f, BM_ELEM_TAG) && /* Face: visible. */
1148 !BM_elem_flag_test(lsub, BM_ELEM_TAG)) /* Loop: unselected. */
1149 {
1150 luv = BM_ELEM_CD_GET_FLOAT_P(lsub, offsets.uv);
1151 add_v2_v2(uv, luv);
1152 uv_tot++;
1153 }
1154 }
1155
1156 if (uv_tot) {
1157 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
1158 mul_v2_v2fl(luv, uv, 1.0f / float(uv_tot));
1159 changed = true;
1160 }
1161 }
1162 }
1163 }
1164 }
1165
1166 return changed;
1167}
1168
1169static bool uv_snap_uvs_to_pixels(SpaceImage *sima, Scene *scene, Object *obedit)
1170{
1172 int width = 0, height = 0;
1173 float w, h;
1174 bool changed = false;
1175
1176 ED_space_image_get_size(sima, &width, &height);
1177 w = float(width);
1178 h = float(height);
1179
1180 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1181 uv_snap_to_pixel(luv, w, h);
1182 changed = true;
1183 });
1184
1185 return changed;
1186}
1187
1189{
1190 Scene *scene = CTX_data_scene(C);
1191 ViewLayer *view_layer = CTX_data_view_layer(C);
1193 const int target = RNA_enum_get(op->ptr, "target");
1194 float offset[2] = {0};
1195
1197 scene, view_layer, nullptr);
1198
1199 if (target == 2) {
1200 float center[2];
1201 if (!ED_uvedit_center_multi(scene, objects, center, sima->around)) {
1202 return OPERATOR_CANCELLED;
1203 }
1204 sub_v2_v2v2(offset, sima->cursor, center);
1205 }
1206
1207 bool changed_multi = false;
1208 for (Object *obedit : objects) {
1210
1211 if (em->bm->totvertsel == 0) {
1212 continue;
1213 }
1214
1215 bool changed = false;
1216 switch (target) {
1217 case 0:
1218 changed = uv_snap_uvs_to_pixels(sima, scene, obedit);
1219 break;
1220 case 1:
1221 changed = uv_snap_uvs_to_cursor(scene, obedit, sima->cursor);
1222 break;
1223 case 2:
1224 changed = uv_snap_uvs_offset(scene, obedit, offset);
1225 break;
1226 case 3:
1227 changed = uv_snap_uvs_to_adjacent_unselected(scene, obedit);
1228 break;
1229 }
1230
1231 if (changed) {
1232 changed_multi = true;
1233 uvedit_live_unwrap_update(sima, scene, obedit);
1234 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1235 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1236 }
1237 }
1238
1239 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1240}
1241
1243{
1244 static const EnumPropertyItem target_items[] = {
1245 {0, "PIXELS", 0, "Pixels", ""},
1246 {1, "CURSOR", 0, "Cursor", ""},
1247 {2, "CURSOR_OFFSET", 0, "Cursor (Offset)", ""},
1248 {3, "ADJACENT_UNSELECTED", 0, "Adjacent Unselected", ""},
1249 {0, nullptr, 0, nullptr, nullptr},
1250 };
1251
1252 /* identifiers */
1253 ot->name = "Snap Selection";
1254 ot->description = "Snap selected UV vertices to target type";
1255 ot->idname = "UV_OT_snap_selected";
1256 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1257
1258 /* API callbacks. */
1259 ot->exec = uv_snap_selection_exec;
1261
1262 /* properties */
1264 ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1265}
1266
1268
1269/* -------------------------------------------------------------------- */
1272
1274{
1275 Scene *scene = CTX_data_scene(C);
1276 ViewLayer *view_layer = CTX_data_view_layer(C);
1277 BMFace *efa;
1278 BMLoop *l;
1279 BMIter iter, liter;
1280 const ToolSettings *ts = scene->toolsettings;
1281 const bool clear = RNA_boolean_get(op->ptr, "clear");
1282 const bool invert = RNA_boolean_get(op->ptr, "invert");
1283 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1284
1286 scene, view_layer, nullptr);
1287
1288 for (Object *obedit : objects) {
1290
1291 bool changed = false;
1292
1293 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1294 if (em->bm->totvertsel == 0) {
1295 continue;
1296 }
1297 if (synced_selection) {
1298 /* Pass. */
1299 }
1300 else {
1301 if (!BM_uv_map_attr_vert_select_exists(em->bm, active_uv_name)) {
1302 continue;
1303 }
1304 }
1305
1306 if (clear && !BM_uv_map_attr_pin_exists(em->bm, active_uv_name)) {
1307 continue;
1308 }
1309
1310 BM_uv_map_attr_pin_ensure(em->bm, active_uv_name);
1311 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1312
1313 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1314 if (!uvedit_face_visible_test(scene, efa)) {
1315 continue;
1316 }
1317
1318 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1319
1320 if (uvedit_uv_select_test(scene, l, offsets)) {
1321 changed = true;
1322 if (invert) {
1323 BM_ELEM_CD_SET_BOOL(l, offsets.pin, !BM_ELEM_CD_GET_BOOL(l, offsets.pin));
1324 }
1325 else {
1326 BM_ELEM_CD_SET_BOOL(l, offsets.pin, !clear);
1327 }
1328 }
1329 }
1330 }
1331
1332 if (changed) {
1333 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1334 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SYNC_TO_EVAL);
1335 }
1336 }
1337
1338 return OPERATOR_FINISHED;
1339}
1340
1342{
1343 PropertyRNA *prop;
1344
1345 /* identifiers */
1346 ot->name = "Pin";
1347 ot->description =
1348 "Set/clear selected UV vertices as anchored between multiple unwrap operations";
1349 ot->idname = "UV_OT_pin";
1350 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1351
1352 /* API callbacks. */
1353 ot->exec = uv_pin_exec;
1354 ot->poll = ED_operator_uvedit;
1355
1356 /* properties */
1357 prop = RNA_def_boolean(
1358 ot->srna, "clear", false, "Clear", "Clear pinning for the selection instead of setting it");
1360 prop = RNA_def_boolean(ot->srna,
1361 "invert",
1362 false,
1363 "Invert",
1364 "Invert pinning for the selection instead of setting it");
1366}
1367
1369
1370/* -------------------------------------------------------------------- */
1373
1374/* check if we are selected or unselected based on 'bool_test' arg,
1375 * needed for select swap support */
1376#define UV_VERT_SEL_TEST(l, bool_test) (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) == bool_test)
1377
1378#define UV_EDGE_SEL_TEST(l, bool_test) (BM_ELEM_CD_GET_BOOL(l, offsets.select_edge) == bool_test)
1379
1380/* is every UV vert selected or unselected depending on bool_test */
1381static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, const BMUVOffsets &offsets)
1382{
1383 BMLoop *l_iter;
1384 BMLoop *l_first;
1385
1386 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1387 do {
1388 if (!UV_EDGE_SEL_TEST(l_iter, select_test)) {
1389 return false;
1390 }
1391 } while ((l_iter = l_iter->next) != l_first);
1392
1393 return true;
1394}
1395
1397{
1398 ViewLayer *view_layer = CTX_data_view_layer(C);
1399 Scene *scene = CTX_data_scene(C);
1400 const ToolSettings *ts = scene->toolsettings;
1401 const bool swap = RNA_boolean_get(op->ptr, "unselected");
1402 const bool use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1403
1405 scene, view_layer, nullptr);
1406
1407 for (Object *ob : objects) {
1409 BMFace *efa;
1410 BMLoop *l;
1411 BMIter iter, liter;
1412
1413 if (ts->uv_flag & UV_SYNC_SELECTION) {
1414 /* Pass. */
1415 }
1416 else {
1417 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata,
1419 BM_uv_map_attr_vert_select_ensure(em->bm, active_uv_name);
1420 BM_uv_map_attr_edge_select_ensure(em->bm, active_uv_name);
1421 }
1422 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1423
1424 if (ts->uv_flag & UV_SYNC_SELECTION) {
1425 if (EDBM_mesh_hide(em, swap)) {
1426 Mesh *mesh = static_cast<Mesh *>(ob->data);
1428 params.calc_looptris = true;
1429 params.calc_normals = false;
1430 params.is_destructive = false;
1431 EDBM_update(mesh, &params);
1432 }
1433 continue;
1434 }
1435
1436 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1437 int hide = 0;
1438
1439 if (!uvedit_face_visible_test(scene, efa)) {
1440 continue;
1441 }
1442
1443 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1444
1446 hide = 1;
1447 break;
1448 }
1449 }
1450
1451 if (hide) {
1452 if (use_face_center) {
1453 if (em->selectmode == SCE_SELECT_FACE) {
1454 /* Deselect BMesh face if UV face is (de)selected depending on #swap. */
1455 if (bm_face_is_all_uv_sel(efa, !swap, offsets)) {
1456 BM_face_select_set(em->bm, efa, false);
1457 }
1458 uvedit_face_select_disable(scene, em->bm, efa, offsets);
1459 }
1460 else {
1461 if (bm_face_is_all_uv_sel(efa, true, offsets) == !swap) {
1462 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1463 /* For both cases rely on edge sel tests, since all vert sel tests are invalid in
1464 * case of sticky selections. */
1465 if (UV_EDGE_SEL_TEST(l, !swap) && (em->selectmode == SCE_SELECT_EDGE)) {
1466 BM_edge_select_set(em->bm, l->e, false);
1467 }
1468 else if (UV_EDGE_SEL_TEST(l, !swap) && (em->selectmode == SCE_SELECT_VERTEX)) {
1469 BM_vert_select_set(em->bm, l->v, false);
1470 }
1471 }
1472 }
1473 if (!swap) {
1474 uvedit_face_select_disable(scene, em->bm, efa, offsets);
1475 }
1476 }
1477 }
1478 else if (em->selectmode == SCE_SELECT_FACE) {
1479 /* Deselect BMesh face depending on the type of UV selectmode and the type of UV element
1480 * being considered. */
1481 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1482 if (UV_EDGE_SEL_TEST(l, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) {
1483 BM_face_select_set(em->bm, efa, false);
1484 break;
1485 }
1487 BM_face_select_set(em->bm, efa, false);
1488 break;
1489 }
1490 if (ts->uv_selectmode == UV_SELECT_ISLAND) {
1491 BM_face_select_set(em->bm, efa, false);
1492 break;
1493 }
1494 }
1495 uvedit_face_select_disable(scene, em->bm, efa, offsets);
1496 }
1497 else {
1498 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1499 if (UV_EDGE_SEL_TEST(l, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) {
1500 if (em->selectmode == SCE_SELECT_EDGE) {
1501 BM_edge_select_set(em->bm, l->e, false);
1502 }
1503 else {
1504 BM_vert_select_set(em->bm, l->v, false);
1505 BM_vert_select_set(em->bm, l->next->v, false);
1506 }
1507 }
1508 else if (UV_VERT_SEL_TEST(l, !swap) && (ts->uv_selectmode != UV_SELECT_EDGE)) {
1509 if (em->selectmode == SCE_SELECT_EDGE) {
1510 BM_edge_select_set(em->bm, l->e, false);
1511 }
1512 else {
1513 BM_vert_select_set(em->bm, l->v, false);
1514 }
1515 }
1516 }
1517 if (!swap) {
1518 uvedit_face_select_disable(scene, em->bm, efa, offsets);
1519 }
1520 }
1521 }
1522 }
1523
1524 /* Flush editmesh selections to ensure valid selection states. */
1525 if (em->selectmode != SCE_SELECT_FACE) {
1526 /* NOTE: Make sure correct flags are used. Previously this was done by passing
1527 * (SCE_SELECT_VERTEX | SCE_SELECT_EDGE), which doesn't work now that we support proper UV
1528 * edge selection. */
1529
1531 }
1532
1534
1535 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
1537 }
1538
1539 return OPERATOR_FINISHED;
1540}
1541
1542#undef UV_VERT_SEL_TEST
1543#undef UV_EDGE_SEL_TEST
1544
1546{
1547 /* identifiers */
1548 ot->name = "Hide Selected";
1549 ot->description = "Hide (un)selected UV vertices";
1550 ot->idname = "UV_OT_hide";
1551 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1552
1553 /* API callbacks. */
1554 ot->exec = uv_hide_exec;
1555 ot->poll = ED_operator_uvedit;
1556
1557 /* props */
1559 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
1560}
1561
1563
1564/* -------------------------------------------------------------------- */
1567
1569{
1570 ViewLayer *view_layer = CTX_data_view_layer(C);
1571 Scene *scene = CTX_data_scene(C);
1572 const ToolSettings *ts = scene->toolsettings;
1573
1574 const bool use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1575 const bool select = RNA_boolean_get(op->ptr, "select");
1576
1578 scene, view_layer, nullptr);
1579
1580 for (Object *ob : objects) {
1582 BMFace *efa;
1583 BMLoop *l;
1584 BMIter iter, liter;
1585
1586 if (ts->uv_flag & UV_SYNC_SELECTION) {
1587 /* Pass. */
1588 }
1589 else {
1590 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata,
1592 BM_uv_map_attr_vert_select_ensure(em->bm, active_uv_name);
1593 BM_uv_map_attr_edge_select_ensure(em->bm, active_uv_name);
1594 }
1595 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1596
1597 /* NOTE: Selecting faces is delayed so that it doesn't select verts/edges and confuse certain
1598 * UV selection checks.
1599 * This creates a temporary state which breaks certain UV selection functions that do face
1600 * visibility checks internally. Current implementation handles each case separately. */
1601
1602 /* call the mesh function if we are in mesh sync sel */
1603 if (ts->uv_flag & UV_SYNC_SELECTION) {
1604 if (EDBM_mesh_reveal(em, select)) {
1605 Mesh *mesh = static_cast<Mesh *>(ob->data);
1607 params.calc_looptris = true;
1608 params.calc_normals = false;
1609 params.is_destructive = false;
1610 EDBM_update(mesh, &params);
1611 }
1612 continue;
1613 }
1614
1615 /* NOTE(@sidd017): Supporting selections in all cases is quite difficult considering there are
1616 * at least 12 cases to look into (3 mesh select-modes + 4 uv select-modes + sticky modes).
1617 * For now we select all UV faces as sticky disabled to ensure proper UV selection states (vert
1618 * + edge flags) */
1619 if (use_face_center) {
1620 if (em->selectmode == SCE_SELECT_FACE) {
1621 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1624 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1627 }
1628 // BM_face_select_set(em->bm, efa, true);
1630 }
1631 }
1632 }
1633 else {
1634 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1637 int totsel = 0;
1638 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1639 if (em->selectmode == SCE_SELECT_VERTEX) {
1640 totsel += BM_elem_flag_test(l->v, BM_ELEM_SELECT);
1641 }
1642 else if (em->selectmode == SCE_SELECT_EDGE) {
1643 totsel += BM_elem_flag_test(l->e, BM_ELEM_SELECT);
1644 }
1645 }
1646
1647 if (!totsel) {
1648 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1649
1652 }
1653 }
1654 // BM_face_select_set(em->bm, efa, true);
1656 }
1657 }
1658 }
1659 }
1660 else if (em->selectmode == SCE_SELECT_FACE) {
1661 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1664 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1667 }
1668 // BM_face_select_set(em->bm, efa, true);
1670 }
1671 }
1672 }
1673 else {
1674 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1677 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1680 }
1681 // BM_face_select_set(em->bm, efa, true);
1683 }
1684 }
1685 }
1686
1687 /* re-select tagged faces */
1689
1690 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
1692 }
1693
1694 return OPERATOR_FINISHED;
1695}
1696
1698{
1699 /* identifiers */
1700 ot->name = "Reveal Hidden";
1701 ot->description = "Reveal all hidden UV vertices";
1702 ot->idname = "UV_OT_reveal";
1703 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1704
1705 /* API callbacks. */
1706 ot->exec = uv_reveal_exec;
1707 ot->poll = ED_operator_uvedit;
1708
1709 RNA_def_boolean(ot->srna, "select", true, "Select", "");
1710}
1711
1713
1714/* -------------------------------------------------------------------- */
1717
1719{
1721
1722 if (!sima) {
1723 return OPERATOR_CANCELLED;
1724 }
1725
1726 RNA_float_get_array(op->ptr, "location", sima->cursor);
1727
1728 {
1729 wmMsgBus *mbus = CTX_wm_message_bus(C);
1730 bScreen *screen = CTX_wm_screen(C);
1731 WM_msg_publish_rna_prop(mbus, &screen->id, sima, SpaceImageEditor, cursor_location);
1732 }
1733
1735
1736 /* Use pass-through to allow click-drag to transform the cursor. */
1738}
1739
1741{
1742 ARegion *region = CTX_wm_region(C);
1743 float location[2];
1744
1745 if (region->regiontype == RGN_TYPE_WINDOW) {
1747 if (sima && ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) {
1748 return OPERATOR_PASS_THROUGH;
1749 }
1750 }
1751
1753 &region->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
1754 RNA_float_set_array(op->ptr, "location", location);
1755
1756 return uv_set_2d_cursor_exec(C, op);
1757}
1758
1760{
1761 /* identifiers */
1762 ot->name = "Set 2D Cursor";
1763 ot->description = "Set 2D cursor location";
1764 ot->idname = "UV_OT_cursor_set";
1765
1766 /* API callbacks. */
1767 ot->exec = uv_set_2d_cursor_exec;
1768 ot->invoke = uv_set_2d_cursor_invoke;
1770
1771 /* properties */
1773 "location",
1774 2,
1775 nullptr,
1776 -FLT_MAX,
1777 FLT_MAX,
1778 "Location",
1779 "Cursor location in normalized (0.0 to 1.0) coordinates",
1780 -10.0f,
1781 10.0f);
1782}
1783
1785
1786/* -------------------------------------------------------------------- */
1789
1791{
1792 Scene *scene = CTX_data_scene(C);
1793 ViewLayer *view_layer = CTX_data_view_layer(C);
1794 const bool mark_seams = RNA_boolean_get(op->ptr, "mark_seams");
1795 const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
1796 bool changed_multi = false;
1797
1799 scene, view_layer, nullptr);
1800
1801 for (Object *ob : objects) {
1802 Mesh *mesh = (Mesh *)ob->data;
1803 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1804 BMesh *bm = em->bm;
1805 BMIter iter;
1806
1807 if (!EDBM_uv_check(em)) {
1808 continue;
1809 }
1810
1811 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1812 bool changed = false;
1813
1814 BMFace *f;
1815 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1816 if (!uvedit_face_visible_test(scene, f)) {
1817 continue;
1818 }
1819
1820 BMLoop *l_iter;
1821 BMLoop *l_first;
1822
1823 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1824 do {
1825 if (l_iter == l_iter->radial_next) {
1826 continue;
1827 }
1828 if (!uvedit_edge_select_test(scene, l_iter, offsets)) {
1829 continue;
1830 }
1831
1832 bool mark = false;
1833 BMLoop *l_other = l_iter->radial_next;
1834 do {
1835 if (!BM_loop_uv_share_edge_check(l_iter, l_other, offsets.uv)) {
1836 mark = true;
1837 break;
1838 }
1839 } while ((l_other = l_other->radial_next) != l_iter);
1840
1841 if (mark) {
1842 if (mark_seams) {
1844 }
1845 if (mark_sharp) {
1847 }
1848 changed = true;
1849 }
1850 } while ((l_iter = l_iter->next) != l_first);
1851 }
1852
1853 if (changed) {
1854 changed_multi = true;
1855 DEG_id_tag_update(&mesh->id, 0);
1857 }
1858 }
1859
1860 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1861}
1862
1864{
1865 /* identifiers */
1866 ot->name = "Seams from Islands";
1867 ot->description = "Set mesh seams according to island setup in the UV editor";
1868 ot->idname = "UV_OT_seams_from_islands";
1869
1870 /* flags */
1871 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1872
1873 /* API callbacks. */
1875 ot->poll = ED_operator_uvedit;
1876
1877 RNA_def_boolean(ot->srna, "mark_seams", true, "Mark Seams", "Mark boundary edges as seams");
1878 RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark boundary edges as sharp");
1879}
1880
1882
1883/* -------------------------------------------------------------------- */
1886
1888{
1889 Scene *scene = CTX_data_scene(C);
1890 ViewLayer *view_layer = CTX_data_view_layer(C);
1891 const ToolSettings *ts = scene->toolsettings;
1892
1893 BMFace *efa;
1894 BMLoop *loop;
1895 BMIter iter, liter;
1896
1897 const bool flag_set = !RNA_boolean_get(op->ptr, "clear");
1898 const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1899
1901 scene, view_layer, nullptr);
1902
1903 bool changed = false;
1904
1905 for (Object *ob : objects) {
1906 Mesh *mesh = (Mesh *)ob->data;
1907 BMEditMesh *em = mesh->runtime->edit_mesh.get();
1908 BMesh *bm = em->bm;
1909
1910 if (synced_selection && (bm->totedgesel == 0)) {
1911 continue;
1912 }
1913
1914 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1915
1916 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1917 if (uvedit_face_visible_test(scene, efa)) {
1918 BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) {
1919 if (uvedit_edge_select_test(scene, loop, offsets)) {
1920 BM_elem_flag_set(loop->e, BM_ELEM_SEAM, flag_set);
1921 changed = true;
1922 }
1923 }
1924 }
1925 }
1926
1927 if (changed) {
1928 DEG_id_tag_update(&mesh->id, 0);
1930 }
1931 }
1932
1933 if (changed) {
1934 ED_uvedit_live_unwrap(scene, objects);
1935 }
1936
1937 return OPERATOR_FINISHED;
1938}
1939
1941{
1942 uiPopupMenu *pup;
1943 uiLayout *layout;
1944
1945 if (RNA_struct_property_is_set(op->ptr, "clear")) {
1946 return uv_mark_seam_exec(C, op);
1947 }
1948
1949 pup = UI_popup_menu_begin(C, IFACE_("Edges"), ICON_NONE);
1950 layout = UI_popup_menu_layout(pup);
1951
1953 PointerRNA op_ptr = layout->op(
1954 op->type->idname, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Mark Seam"), ICON_NONE);
1955 RNA_boolean_set(&op_ptr, "clear", false);
1956 op_ptr = layout->op(
1957 op->type->idname, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Clear Seam"), ICON_NONE);
1958 RNA_boolean_set(&op_ptr, "clear", true);
1959
1960 UI_popup_menu_end(C, pup);
1961
1962 return OPERATOR_INTERFACE;
1963}
1964
1966{
1967 /* identifiers */
1968 ot->name = "Mark Seam";
1969 ot->description = "Mark selected UV edges as seams";
1970 ot->idname = "UV_OT_mark_seam";
1971
1972 /* flags */
1973 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1974
1975 /* API callbacks. */
1976 ot->exec = uv_mark_seam_exec;
1977 ot->invoke = uv_mark_seam_invoke;
1978 ot->poll = ED_operator_uvedit;
1979
1980 RNA_def_boolean(ot->srna, "clear", false, "Clear Seams", "Clear instead of marking seams");
1981}
1982
1984
1985/* -------------------------------------------------------------------- */
1988
1990{
1991 /* `uvedit_select.cc` */
2008
2011
2013
2018
2024
2035
2040
2042}
2043
2045{
2047 wmOperatorTypeMacro *otmacro;
2048
2049 ot = WM_operatortype_append_macro("UV_OT_rip_move",
2050 "UV Rip Move",
2051 "Unstitch UVs and move the result",
2053 WM_operatortype_macro_define(ot, "UV_OT_rip");
2054 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
2055 RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
2056 RNA_boolean_set(otmacro->ptr, "mirror", false);
2057}
2058
2060{
2061 wmKeyMap *keymap;
2062
2063 keymap = WM_keymap_ensure(keyconf, "UV Editor", SPACE_EMPTY, RGN_TYPE_WINDOW);
2064 keymap->poll = ED_operator_uvedit;
2065}
2066
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: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)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
General operations, lookup, etc. for materials.
Material * BKE_object_material_get(Object *ob, short act)
Material * BKE_object_material_get_eval(Object *ob, short act)
#define SH_NODE_TEX_IMAGE
#define SH_NODE_TEX_ENVIRONMENT
#define BLI_assert(a)
Definition BLI_assert.h:46
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:365
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(const 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.
@ OB_MESH
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ UV_SYNC_SELECTION
@ UV_SELECT_VERTEX
@ UV_SELECT_FACE
@ UV_SELECT_EDGE
@ UV_SELECT_ISLAND
@ RGN_TYPE_WINDOW
@ SI_LIVE_UNWRAP
@ SPACE_EMPTY
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
@ 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)
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)
void uvedit_face_select_disable(const Scene *scene, BMesh *bm, BMFace *efa, const BMUVOffsets &offsets)
void ED_uvedit_live_unwrap_re_solve()
bool uvedit_uv_select_test(const Scene *scene, const BMLoop *l, const BMUVOffsets &offsets)
bool uvedit_edge_select_test(const Scene *scene, const BMLoop *l, const BMUVOffsets &offsets)
void ED_uvedit_live_unwrap_end(bool cancel)
bool uvedit_face_visible_test(const Scene *scene, const BMFace *efa)
void ED_uvedit_live_unwrap(const Scene *scene, blender::Span< Object * > objects)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
#define C
Definition RandGen.cpp:29
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:1667
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DATA
Definition WM_types.hh:506
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SPACE_IMAGE
Definition WM_types.hh:519
#define ND_SELECT
Definition WM_types.hh:505
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:245
#define NC_SPACE
Definition WM_types.hh:389
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#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_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#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_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)
bool BM_uv_map_attr_vert_select_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)
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_FACE
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 void * element
ATTR_WARN_UNUSED_RESULT const BMLoop * l
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)
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
int64_t size() const
void append(const T &value)
IndexRange index_range() const
void reserve(const int64_t min_capacity)
KDTree_3d * tree
#define select(A, B, C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
CCL_NAMESPACE_BEGIN ccl_device float invert(const float color, const float factor)
Definition invert.h:11
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void clear(Message &msg)
Definition msgfmt.cc:213
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 swap(a, b)
Definition sort.cc:59
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
short selectmode
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
int totvertsel
int totloop
CustomData ldata
Definition DNA_ID.h:404
struct bNodeTree * nodetree
MeshRuntimeHandle * runtime
CustomData corner_data
struct ToolSettings * toolsettings
float cursor[2]
UvElement * storage
struct ID * id
int16_t type_legacy
void * storage
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
int mval[2]
Definition WM_types.hh:760
bool(* poll)(struct bContext *)
const char * idname
Definition WM_types.hh:1032
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
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 bool uvedit_uv_straighten_elements(const UvElement *element, const int len, const BMUVOffsets &offsets, const eUVWeldAlign tool)
static wmOperatorStatus uv_seams_from_islands_exec(bContext *C, wmOperator *op)
static wmOperatorStatus uv_mark_seam_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void UV_OT_cursor_set(wmOperatorType *ot)
eUVEndPointPrecedence
@ UVEP_PINNED
@ UVEP_SELECTED
@ UVEP_INVALID
void ED_operatortypes_uvedit()
static wmOperatorStatus uv_snap_selection_exec(bContext *C, wmOperator *op)
static void UV_OT_align(wmOperatorType *ot)
static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool)
static wmOperatorStatus uv_remove_doubles_to_selected_shared_vertex(bContext *C, wmOperator *op)
static void uv_snap_cursor_to_origin(float uvco[2])
static wmOperatorStatus uv_remove_doubles_to_selected(bContext *C, wmOperator *op)
static wmOperatorStatus uv_snap_cursor_exec(bContext *C, wmOperator *op)
static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, const BMUVOffsets &offsets)
static void UV_OT_mark_seam(wmOperatorType *ot)
static void UV_OT_weld(wmOperatorType *ot)
static wmOperatorStatus uv_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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:71
static void uv_weld_align(bContext *C, eUVWeldAlign tool)
static wmOperatorStatus uv_weld_exec(bContext *C, wmOperator *)
static wmOperatorStatus uv_hide_exec(bContext *C, wmOperator *op)
static bool uvedit_uv_align_weld(Scene *scene, BMesh *bm, const eUVWeldAlign tool, const float cent[2])
static wmOperatorStatus uv_remove_doubles_exec(bContext *C, wmOperator *op)
void ED_uvedit_select_all(BMesh *bm)
static void UV_OT_snap_selected(wmOperatorType *ot)
static wmOperatorStatus uv_reveal_exec(bContext *C, wmOperator *op)
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 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 bool uvedit_median_multi(const Scene *scene, const Span< Object * > objects_edit, float co[2])
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 void UV_OT_hide(wmOperatorType *ot)
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 wmOperatorStatus uv_mark_seam_exec(bContext *C, wmOperator *op)
static wmOperatorStatus uv_remove_doubles_to_unselected(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 wmOperatorStatus uv_align_exec(bContext *C, wmOperator *op)
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:90
#define UV_VERT_SEL_TEST(l, bool_test)
static wmOperatorStatus uv_set_2d_cursor_exec(bContext *C, wmOperator *op)
static void UV_OT_pin(wmOperatorType *ot)
void ED_operatormacros_uvedit()
static void UV_OT_remove_doubles(wmOperatorType *ot)
static bool is_image_texture_node(bNode *node)
static wmOperatorStatus uv_pin_exec(bContext *C, wmOperator *op)
uint len
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4225
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:893
#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)