Blender V5.0
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_bounds.hh"
24#include "BLI_kdtree.h"
25#include "BLI_math_base.hh"
26#include "BLI_math_geom.h"
27#include "BLI_math_vector.h"
28#include "BLI_math_vector.hh"
29#include "BLI_utildefines.h"
30
31#include "BLT_translation.hh"
32
33#include "BKE_context.hh"
34#include "BKE_customdata.hh"
35#include "BKE_editmesh.hh"
36#include "BKE_layer.hh"
38#include "BKE_material.hh"
39#include "BKE_mesh_mapping.hh"
40#include "BKE_mesh_types.hh"
41#include "BKE_node.hh"
43
44#include "DEG_depsgraph.hh"
46
47#include "ED_image.hh"
48#include "ED_mesh.hh"
49#include "ED_node.hh"
50#include "ED_screen.hh"
51#include "ED_uvedit.hh"
52
53#include "RNA_access.hh"
54#include "RNA_define.hh"
55
56#include "WM_api.hh"
57#include "WM_message.hh"
58#include "WM_types.hh"
59
60#include "UI_interface.hh"
62#include "UI_resources.hh"
63#include "UI_view2d.hh"
64
65#include "uvedit_intern.hh"
66
67using namespace blender;
68
69/* -------------------------------------------------------------------- */
72
74{
75 BMEditMesh *em;
76 int ret;
77
78 if (!obedit) {
79 return false;
80 }
81
82 if (obedit->type != OB_MESH) {
83 return false;
84 }
85
86 em = BKE_editmesh_from_object(obedit);
87 ret = EDBM_uv_check(em);
88
89 return ret;
90}
91
93{
95
96 if (ob && ob->type == OB_MESH) {
97 Mesh *mesh = static_cast<Mesh *>(ob->data);
98
99 if (CustomData_get_layer(&mesh->corner_data, CD_PROP_FLOAT2) != nullptr) {
100 return 1;
101 }
102 }
103
104 return 0;
105}
106
108
109/* -------------------------------------------------------------------- */
112
117
119 int mat_nr,
120 Image **r_ima,
121 ImageUser **r_iuser,
122 const bNode **r_node,
123 const bNodeTree **r_ntree)
124{
126 BKE_object_material_get(ob, mat_nr);
127 bNodeTree *ntree = ma ? ma->nodetree : nullptr;
128 bNode *node = (ntree) ? bke::node_get_active_texture(*ntree) : nullptr;
129
130 if (node && is_image_texture_node(node)) {
131 if (r_ima) {
132 *r_ima = (Image *)node->id;
133 }
134 if (r_iuser) {
135 if (node->type_legacy == SH_NODE_TEX_IMAGE) {
136 *r_iuser = &((NodeTexImage *)node->storage)->iuser;
137 }
138 else if (node->type_legacy == SH_NODE_TEX_ENVIRONMENT) {
139 *r_iuser = &((NodeTexEnvironment *)node->storage)->iuser;
140 }
141 else {
142 *r_iuser = nullptr;
143 }
144 }
145 if (r_node) {
146 *r_node = node;
147 }
148 if (r_ntree) {
149 *r_ntree = ntree;
150 }
151 return true;
152 }
153
154 if (r_ima) {
155 *r_ima = nullptr;
156 }
157 if (r_iuser) {
158 *r_iuser = nullptr;
159 }
160 if (r_node) {
161 *r_node = node;
162 }
163 if (r_ntree) {
164 *r_ntree = ntree;
165 }
166
167 return false;
168}
169
170void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima)
171{
172 Material *ma = BKE_object_material_get(ob, mat_nr);
173 bNode *node = ma ? bke::node_get_active_texture(*ma->nodetree) : nullptr;
174
175 if (node && is_image_texture_node(node)) {
176 node->id = &ima->id;
178 }
179}
180
182
183/* -------------------------------------------------------------------- */
186
188{
189 if (sima && (sima->flag & SI_LIVE_UNWRAP)) {
190 ED_uvedit_live_unwrap_begin(scene, obedit, nullptr);
193 }
194}
195
197
198/* -------------------------------------------------------------------- */
201
202void ED_uvedit_foreach_uv(const Scene *scene,
203 BMesh *bm,
204 const bool skip_invisible,
205 const bool selected,
206 FunctionRef<void(float[2])> user_fn)
207{
208 /* Check selection for quick return. */
209 const bool synced_selection = (scene->toolsettings->uv_flag & UV_FLAG_SELECT_SYNC) != 0;
210 if (synced_selection && bm->totvertsel == (selected ? 0 : bm->totvert)) {
211 return;
212 }
213
214 BMFace *efa;
215 BMLoop *l;
216 BMIter iter, liter;
217
218 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
219
220 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
221 if (skip_invisible && !uvedit_face_visible_test(scene, efa)) {
222 continue;
223 }
224
225 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
226 if (uvedit_uv_select_test(scene, bm, l, offsets) == selected) {
227 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
228 user_fn(luv);
229 }
230 }
231 }
232}
233
235 const Span<Object *> objects_edit,
236 const bool skip_invisible,
237 const bool skip_nonselected,
238 FunctionRef<void(float[2])> user_fn)
239{
240 for (Object *obedit : objects_edit) {
242 ED_uvedit_foreach_uv(scene, em->bm, skip_invisible, skip_nonselected, user_fn);
243 }
244}
245
247 const Span<Object *> objects_edit,
248 float r_min[2],
249 float r_max[2])
250{
251 bool changed = false;
252 INIT_MINMAX2(r_min, r_max);
253 ED_uvedit_foreach_uv_multi(scene, objects_edit, true, true, [&](float luv[2]) {
254 minmax_v2v2_v2(r_min, r_max, luv);
255 changed = true;
256 });
257 return changed;
258}
259
261{
262 BMFace *efa;
263 BMLoop *l;
264 BMIter iter, liter;
265
266 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
267 uvedit_face_select_set_no_sync(ts, bm, efa, true);
268 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
271 }
272 }
273}
274
275static bool uvedit_median_multi(const Scene *scene, const Span<Object *> objects_edit, float co[2])
276{
277 uint sel = 0;
278 zero_v2(co);
279
280 ED_uvedit_foreach_uv_multi(scene, objects_edit, true, true, [&](float luv[2]) {
281 add_v2_v2(co, luv);
282 sel++;
283 });
284
285 mul_v2_fl(co, 1.0f / float(sel));
286
287 return (sel != 0);
288}
289
291 Span<Object *> objects_edit,
292 float cent[2],
293 char mode)
294{
295 bool changed = false;
296
297 if (mode == V3D_AROUND_CENTER_BOUNDS) { /* bounding box */
298 float min[2], max[2];
299 if (ED_uvedit_minmax_multi(scene, objects_edit, min, max)) {
300 mid_v2_v2v2(cent, min, max);
301 changed = true;
302 }
303 }
304 else {
305 if (uvedit_median_multi(scene, objects_edit, cent)) {
306 changed = true;
307 }
308 }
309
310 return changed;
311}
312
314 Scene *scene,
315 ViewLayer *view_layer,
316 float r_center[2],
317 char mode,
318 bool *r_has_select)
319{
320 bool changed = false;
321 switch (mode) {
322 case V3D_AROUND_CURSOR: {
323 copy_v2_v2(r_center, sima->cursor);
324 changed = true;
325 if (r_has_select != nullptr) {
326 Vector<Object *> objects =
328 scene, view_layer, nullptr);
329 *r_has_select = uvedit_select_is_any_selected_multi(scene, objects);
330 }
331 break;
332 }
333 default: {
334 Vector<Object *> objects =
336 scene, view_layer, nullptr);
337 changed = ED_uvedit_center_multi(scene, objects, r_center, mode);
338 if (r_has_select != nullptr) {
339 *r_has_select = changed;
340 }
341 break;
342 }
343 }
344 return changed;
345}
346
347enum class UVMoveType {
349 Pixel = 1,
350 Udim = 2,
351};
352enum class UVMoveDirection {
353 X = 0,
354 Y = 1,
355};
356
358
359{
360 Scene *scene = CTX_data_scene(C);
361 ViewLayer *view_layer = CTX_data_view_layer(C);
364 scene, view_layer, nullptr);
365 UVMoveType type = UVMoveType(RNA_enum_get(op->ptr, "type"));
366 UVMoveDirection axis = UVMoveDirection(RNA_enum_get(op->ptr, "axis"));
367 int distance = RNA_int_get(op->ptr, "distance");
368
369 int size[2];
370 ED_space_image_get_size(sima, &size[0], &size[1]);
371 float distance_final;
372 if (type == UVMoveType::Dynamic) {
373 distance_final = float(distance) / sima->custom_grid_subdiv[int(axis)];
374 }
375 else if (type == UVMoveType::Pixel) {
376 distance_final = float(distance) / size[int(axis)];
377 }
378 else {
379 distance_final = distance;
380 }
381 for (Object *obedit : objects) {
383 bool changed = false;
384 if (em->bm->totvertsel == 0) {
385 continue;
386 }
387
389 scene, em->bm, true, true, [&axis, &distance_final, &changed](float luv[2]) {
390 luv[int(axis)] += distance_final;
391 changed = true;
392 });
393
394 if (changed) {
395 uvedit_live_unwrap_update(sima, scene, obedit);
396 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
397 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
398 }
399 }
400 return OPERATOR_FINISHED;
401}
402
404{
405 static const EnumPropertyItem shift_items[] = {
406 {int(UVMoveType::Dynamic), "DYNAMIC", 0, "Dynamic", "Move by dynamic grid"},
407 {int(UVMoveType::Pixel), "PIXEL", 0, "Pixel", "Move by pixel"},
408 {int(UVMoveType::Udim), "UDIM", 0, "UDIM", "Move by UDIM"},
409 {0, nullptr, 0, nullptr, nullptr},
410 };
411
412 static const EnumPropertyItem axis_items[] = {
413 {int(UVMoveDirection::X), "X", 0, "X axis", "Move vertices on the X axis"},
414 {int(UVMoveDirection::Y), "Y", 0, "Y axis", "Move vertices on the Y axis"},
415 {0, nullptr, 0, nullptr, nullptr},
416 };
417
418 /* identifiers */
419 ot->name = "Move on Axis";
420 ot->description = "Move UVs on an axis";
421 ot->idname = "UV_OT_move_on_axis";
423
424 /* API callbacks. */
425 ot->exec = uv_move_on_axis_exec;
426 ot->poll = ED_operator_uvedit;
427
428 /* properties */
429 RNA_def_enum(ot->srna, "type", shift_items, int(UVMoveType::Udim), "Type", "Move Type");
431 ot->srna, "axis", axis_items, int(UVMoveDirection::X), "Axis", "Axis to move UVs on");
432 RNA_def_int(ot->srna,
433 "distance",
434 1,
435 INT_MIN,
436 INT_MAX,
437 "Distance",
438 "Distance to move UVs",
439 INT_MIN,
440 INT_MAX);
441}
442
443
444/* -------------------------------------------------------------------- */
447
458 Mean = 0,
459 Min = 1,
460 Max = 2,
461};
462
463static bool uvedit_uv_align_weld(Scene *scene,
464 BMesh *bm,
465 const eUVWeldAlign tool,
466 const float cent[2])
467{
468 bool changed = false;
469
470 ED_uvedit_foreach_uv(scene, bm, true, true, [&](float luv[2]) {
471 if (ELEM(tool, UV_ALIGN_X, UV_WELD)) {
472 if (luv[0] != cent[0]) {
473 luv[0] = cent[0];
474 changed = true;
475 }
476 }
477 if (ELEM(tool, UV_ALIGN_Y, UV_WELD)) {
478 if (luv[1] != cent[1]) {
479 luv[1] = cent[1];
480 changed = true;
481 }
482 }
483 });
484
485 return changed;
486}
487
491 UVEP_SELECTED = (1 << 0),
492 UVEP_PINNED = (1 << 1), /* i.e. Pinned verts are preferred to selected. */
493};
495
497{
499 if (pinned) {
500 precedence |= UVEP_PINNED;
501 }
502 return precedence;
503}
504
509static bool uvedit_line_update_endpoint(const float *luv,
510 const bool pinned,
511 float uv_a[2],
512 eUVEndPointPrecedence *prec_a,
513 float uv_b[2],
514 eUVEndPointPrecedence *prec_b)
515{
517
518 float len_sq_a = len_squared_v2v2(uv_a, luv);
519 float len_sq_b = len_squared_v2v2(uv_b, luv);
520
521 /* Caching the value of `len_sq_ab` is unlikely to be faster than recalculating.
522 * Profile before optimizing. */
523 float len_sq_ab = len_squared_v2v2(uv_a, uv_b);
524
525 if ((*prec_a < flags && 0.0f < len_sq_b) || (*prec_a == flags && len_sq_ab < len_sq_b)) {
526 *prec_a = flags;
527 copy_v2_v2(uv_a, luv);
528 return true;
529 }
530
531 if ((*prec_b < flags && 0.0f < len_sq_a) || (*prec_b == flags && len_sq_ab < len_sq_a)) {
532 *prec_b = flags;
533 copy_v2_v2(uv_b, luv);
534 return true;
535 }
536
537 return false;
538}
539
545 const int len,
546 const BMUVOffsets &offsets,
547 const eUVWeldAlign tool)
548{
549 float uv_start[2];
550 float uv_end[2];
553
554 /* Find start and end of line. */
555 for (int i = 0; i < 10; i++) { /* Heuristic to prevent infinite loop. */
556 bool update = false;
557 for (int j = 0; j < len; j++) {
558 float *luv = BM_ELEM_CD_GET_FLOAT_P(element[j].l, offsets.uv);
559 bool pinned = BM_ELEM_CD_GET_BOOL(element[j].l, offsets.pin);
560 update |= uvedit_line_update_endpoint(luv, pinned, uv_start, &prec_start, uv_end, &prec_end);
561 }
562 if (!update) {
563 break;
564 }
565 }
566
567 if (prec_start == UVEP_INVALID || prec_end == UVEP_INVALID) {
568 return false; /* Unable to find two endpoints. */
569 }
570
571 float a = 0.0f; /* Similar to "slope". */
572 eUVWeldAlign tool_local = tool;
573
574 if (tool_local == UV_STRAIGHTEN_X) {
575 if (uv_start[1] == uv_end[1]) {
576 /* Caution, different behavior outside line segment. */
577 tool_local = UV_STRAIGHTEN;
578 }
579 else {
580 a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
581 }
582 }
583 else if (tool_local == UV_STRAIGHTEN_Y) {
584 if (uv_start[0] == uv_end[0]) {
585 /* Caution, different behavior outside line segment. */
586 tool_local = UV_STRAIGHTEN;
587 }
588 else {
589 a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
590 }
591 }
592
593 bool changed = false;
594 for (int j = 0; j < len; j++) {
595 float *luv = BM_ELEM_CD_GET_FLOAT_P(element[j].l, offsets.uv);
596 /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
597 * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
598 * Maybe this should be a BLI func? Or is it already existing?
599 * Could use interp_v2_v2v2, but not sure it's worth it here. */
600 if (tool_local == UV_STRAIGHTEN_X) {
601 luv[0] = a * (luv[1] - uv_start[1]) + uv_start[0];
602 }
603 else if (tool_local == UV_STRAIGHTEN_Y) {
604 luv[1] = a * (luv[0] - uv_start[0]) + uv_start[1];
605 }
606 else {
607 closest_to_line_segment_v2(luv, luv, uv_start, uv_end);
608 }
609 changed = true; /* TODO: Did the UV actually move? */
610 }
611 return changed;
612}
613
617static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool)
618{
619 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
620 if (offsets.uv == -1) {
621 return false;
622 }
623
624 UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true, true);
625 if (element_map == nullptr) {
626 return false;
627 }
628
629 bool changed = false;
630 for (int i = 0; i < element_map->total_islands; i++) {
631 changed |= uvedit_uv_straighten_elements(element_map->storage + element_map->island_indices[i],
632 element_map->island_total_uvs[i],
633 offsets,
634 tool);
635 }
636
637 BM_uv_element_map_free(element_map);
638 return changed;
639}
647 X = 0,
648 Y = 1,
649};
651 Max = 0,
652 Min = 1,
654 None = 3,
655};
661
666
671static bool uvedit_uv_islands_arrange(const Scene *scene,
672 BMesh *bm,
673 const UVAlignIslandAxis axis,
674 const UVAlignIslandMode align,
675 const UVAlignIslandOrder order,
676 const float margin,
677 float2 &position)
678{
679 bool changed = false;
680 UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true, true);
681 if (element_map == nullptr) {
682 return changed;
683 }
684
685 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
686 const uint other_axis = (uint(axis) + 1) % 2;
687 Array<UVAlignIslandBounds> island_bounds_all(element_map->total_islands);
688 for (int i = 0; i < element_map->total_islands; i++) {
689 UvElement *element = element_map->storage + element_map->island_indices[i];
690 UVAlignIslandBounds &island_bounds = island_bounds_all[i];
691 INIT_MINMAX2(island_bounds.bounds.min, island_bounds.bounds.max);
692 for (int j = 0; j < element_map->island_total_uvs[i]; j++) {
693 float *luv = BM_ELEM_CD_GET_FLOAT_P(element[j].l, offsets.uv);
694 minmax_v2v2_v2(island_bounds.bounds.min, island_bounds.bounds.max, luv);
695 }
696 island_bounds.index = i;
697 }
698 std::stable_sort(island_bounds_all.begin(),
699 island_bounds_all.end(),
700 [&order, &axis](const UVAlignIslandBounds &a, const UVAlignIslandBounds &b) {
701 if (order == UVAlignIslandOrder::Fixed) {
702 return a.bounds.min[int(axis)] < b.bounds.min[int(axis)];
703 }
704 const float area_a = (a.bounds.size()[0] * a.bounds.size()[1]);
705 const float area_b = (b.bounds.size()[0] * b.bounds.size()[1]);
706 return (order == UVAlignIslandOrder::LargeToSmall) ? (area_a >= area_b) :
707 (area_a < area_b);
708 });
709
710 for (const UVAlignIslandBounds &island_bounds : island_bounds_all) {
711 UvElement *element = element_map->storage + element_map->island_indices[island_bounds.index];
712 for (int j = 0; j < element_map->island_total_uvs[island_bounds.index]; j++) {
713 float *luv = BM_ELEM_CD_GET_FLOAT_P(element[j].l, offsets.uv);
714 if (align == UVAlignIslandMode::Min) {
715 luv[other_axis] += position[other_axis] - island_bounds.bounds.min[other_axis];
716 }
717 else if (align == UVAlignIslandMode::Center) {
718 luv[other_axis] += position[other_axis] - island_bounds.bounds.center()[other_axis];
719 }
720 else if (align == UVAlignIslandMode::Max) {
721 luv[other_axis] += position[other_axis] - island_bounds.bounds.max[other_axis];
722 }
723 luv[int(axis)] += position[int(axis)] - island_bounds.bounds.min[int(axis)];
724 }
725 position[int(axis)] += island_bounds.bounds.size()[int(axis)] + margin;
726 changed = true;
727 }
728 BM_uv_element_map_free(element_map);
729 return changed;
730}
731
733{
734 Scene *scene = CTX_data_scene(C);
735 ViewLayer *view_layer = CTX_data_view_layer(C);
737
739 scene, view_layer, nullptr);
740
741 const UVAlignInitialPosition initial_position = UVAlignInitialPosition(
742 RNA_enum_get(op->ptr, "initial_position"));
743 const UVAlignIslandAxis axis = UVAlignIslandAxis(RNA_enum_get(op->ptr, "axis"));
744 const UVAlignIslandMode align = UVAlignIslandMode(RNA_enum_get(op->ptr, "align"));
745 const UVAlignIslandOrder order = UVAlignIslandOrder(RNA_enum_get(op->ptr, "order"));
746 const float margin = RNA_float_get(op->ptr, "margin");
747 const uint other_axis = (uint(axis) + 1) % 2;
748
749 float2 position = {0.0f, 0.0f};
750 Bounds<float2> bounds = {{0.0f, 0.0f}, {1.0f, 1.0f}};
751 if (initial_position == UVAlignInitialPosition::BoundingBox) {
752 INIT_MINMAX2(bounds.min, bounds.max);
753 for (Object *obedit : objects) {
755 ED_uvedit_foreach_uv(scene, bm, true, true, [&](float luv[2]) {
756 minmax_v2v2_v2(bounds.min, bounds.max, luv);
757 });
758 }
759 }
760 else if (initial_position == UVAlignInitialPosition::ActiveUDIM) {
761 if (sima && sima->image && (sima->image->source == IMA_SRC_TILED)) {
762 const int tile_x = sima->image->active_tile_index % 10;
763 const int tile_y = sima->image->active_tile_index / 10;
764 bounds.min[0] = tile_x;
765 bounds.min[1] = tile_y;
766 bounds.max[0] = tile_x + 1.0f;
767 bounds.max[1] = tile_y + 1.0f;
768 }
769 }
770 else if (initial_position == UVAlignInitialPosition::UVTileGrid) {
771 /* Leave the minimum at zero. */
772 bounds.max[0] = sima->tile_grid_shape[0];
773 bounds.max[1] = sima->tile_grid_shape[1];
774 }
775 else {
776 if (sima) {
777 position = sima->cursor;
778 }
779 }
780 if (ELEM(initial_position,
784 {
785 if (align == UVAlignIslandMode::Min) {
786 position[other_axis] = bounds.min[other_axis];
787 }
788 else if (align == UVAlignIslandMode::Center) {
789 position[other_axis] = bounds.center()[other_axis];
790 }
791 else {
792 position[other_axis] = bounds.max[other_axis];
793 }
794 position[int(axis)] = bounds.min[int(axis)];
795 }
796 for (Object *obedit : objects) {
798
799 if (em->bm->totvertsel == 0) {
800 continue;
801 }
802 if (uvedit_uv_islands_arrange(scene, em->bm, axis, align, order, margin, position)) {
803 uvedit_live_unwrap_update(sima, scene, obedit);
804 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
805 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
806 }
807 }
808 return OPERATOR_FINISHED;
809}
810
812{
813 static const EnumPropertyItem initial_position_items[] = {
815 "BOUNDING_BOX",
816 0,
817 "Bounding Box",
818 "Initial alignment based on the islands bounding box"},
820 "UV_GRID",
821 0,
822 "UV Grid",
823 "Initial alignment based on UV Tile Grid"},
825 "ACTIVE_UDIM",
826 0,
827 "Active UDIM",
828 "Initial alignment based on Active UDIM"},
830 "CURSOR",
831 0,
832 "2D Cursor",
833 "Initial alignment based on 2D cursor"},
834 {0, nullptr, 0, nullptr, nullptr},
835
836 };
837 static const EnumPropertyItem axis_items[] = {
838 {int(UVAlignIslandAxis::X), "X", 0, "X", "Align UV islands along the X axis"},
839 {int(UVAlignIslandAxis::Y), "Y", 0, "Y", "Align UV islands along the Y axis"},
840 {0, nullptr, 0, nullptr, nullptr},
841 };
842 static const EnumPropertyItem align_items[] = {
843 {int(UVAlignIslandMode::Min), "MIN", 0, "Min", "Align the islands to the min of the island"},
845 "MAX",
846 0,
847 "Max",
848 "Align the islands to the left side of the island"},
850 "CENTER",
851 0,
852 "Center",
853 "Align the islands to the center of the largest island"},
854 {int(UVAlignIslandMode::None), "NONE", 0, "None", "Preserve island alignment"},
855 {0, nullptr, 0, nullptr, nullptr},
856 };
857
858 static const EnumPropertyItem sort_items[] = {
860 "LARGE_TO_SMALL",
861 0,
862 "Largest to Smallest",
863 "Sort islands from largest to smallest"},
865 "SMALL_TO_LARGE",
866 0,
867 "Smallest to Largest",
868 "Sort islands from smallest to largest"},
869 {int(UVAlignIslandOrder::Fixed), "Fixed", 0, "Fixed", "Preserve island order"},
870 {0, nullptr, 0, nullptr, nullptr},
871 };
872 /* identifiers */
873 ot->name = "Arrange/Align Islands";
874 ot->description = "Arrange selected UV islands on a line";
875 ot->idname = "UV_OT_arrange_islands";
877
878 /* API callbacks. */
880 ot->poll = ED_operator_uvedit;
881
882 /* properties */
883 RNA_def_enum(ot->srna,
884 "initial_position",
885 initial_position_items,
887 "Initial Position",
888 "Initial position to arrange islands from");
889 RNA_def_enum(ot->srna,
890 "axis",
891 axis_items,
893 "Axis",
894 "Axis to arrange UV islands on");
895 RNA_def_enum(ot->srna,
896 "align",
897 align_items,
899 "Align",
900 "Location to align islands on");
901 RNA_def_enum(ot->srna,
902 "order",
903 sort_items,
905 "Order",
906 "Order of islands");
907
909 ot->srna, "margin", 0.05f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
910}
911
912static void uv_weld(bContext *C)
913{
914 Scene *scene = CTX_data_scene(C);
915 ViewLayer *view_layer = CTX_data_view_layer(C);
917 float cent[2];
918
920 scene, view_layer, nullptr);
921
922 ED_uvedit_center_multi(scene, objects, cent, 0);
923
924 for (Object *obedit : objects) {
926 bool changed = false;
927
928 if (em->bm->totvertsel == 0) {
929 continue;
930 }
931
932 changed |= uvedit_uv_align_weld(scene, em->bm, UV_WELD, cent);
933
934 if (changed) {
935 uvedit_live_unwrap_update(sima, scene, obedit);
936 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
937 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
938 }
939 }
940}
941
942static void uv_align(bContext *C, eUVWeldAlign tool, UVAlignPositionMode position_mode)
943{
944 Scene *scene = CTX_data_scene(C);
945 ViewLayer *view_layer = CTX_data_view_layer(C);
947 float pos[2], min[2], max[2];
948 const bool align_auto = (tool == UV_ALIGN_AUTO);
950
952 scene, view_layer, nullptr);
953
954 if (tool == UV_ALIGN_AUTO) {
956 scene, objects, true, true, [&](float luv[2]) { minmax_v2v2_v2(min, max, luv); });
957 tool = (max[0] - min[0] >= max[1] - min[1]) ? UV_ALIGN_Y : UV_ALIGN_X;
958 }
959
960 if (!align_auto && ELEM(tool, UV_ALIGN_X, UV_ALIGN_Y) &&
962 {
963 ED_uvedit_minmax_multi(scene, objects, min, max);
964 if (position_mode == UVAlignPositionMode::Min) {
965 pos[0] = min[0];
966 pos[1] = min[1];
967 }
968 else {
969 pos[0] = max[0];
970 pos[1] = max[1];
971 }
972 }
973 else {
975 }
976
977 for (Object *obedit : objects) {
979 bool changed = false;
980
981 if (em->bm->totvertsel == 0) {
982 continue;
983 }
984
986 changed |= uvedit_uv_align_weld(scene, em->bm, tool, pos);
987 }
988
990 changed |= uvedit_uv_straighten(scene, em->bm, tool);
991 }
992
993 if (changed) {
994 uvedit_live_unwrap_update(sima, scene, obedit);
995 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
996 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
997 }
998 }
999}
1001{
1002 uv_align(C,
1003 eUVWeldAlign(RNA_enum_get(op->ptr, "axis")),
1004 UVAlignPositionMode(RNA_enum_get(op->ptr, "position_mode")));
1005
1006 return OPERATOR_FINISHED;
1007}
1008
1009static bool uv_align_poll_property(const bContext * /*C*/, wmOperator *op, const PropertyRNA *prop)
1010{
1011 const char *prop_id = RNA_property_identifier(prop);
1012
1013 if (STREQ(prop_id, "position_mode")) {
1014 int axis = RNA_enum_get(op->ptr, "axis");
1015 if (!ELEM(axis, UV_ALIGN_X, UV_ALIGN_Y)) {
1016 return false;
1017 }
1018 }
1019 return true;
1020}
1022{
1023 static const EnumPropertyItem axis_items[] = {
1025 "ALIGN_S",
1026 0,
1027 "Straighten",
1028 "Align UV vertices along the line defined by the endpoints"},
1030 "ALIGN_T",
1031 0,
1032 "Straighten X",
1033 "Align UV vertices, moving them horizontally to the line defined by the endpoints"},
1035 "ALIGN_U",
1036 0,
1037 "Straighten Y",
1038 "Align UV vertices, moving them vertically to the line defined by the endpoints"},
1040 "ALIGN_AUTO",
1041 0,
1042 "Align Auto",
1043 "Automatically choose the direction on which there is most alignment already"},
1044 {UV_ALIGN_X, "ALIGN_X", 0, "Align Vertically", "Align UV vertices on a vertical line"},
1045 {UV_ALIGN_Y, "ALIGN_Y", 0, "Align Horizontally", "Align UV vertices on a horizontal line"},
1046 {0, nullptr, 0, nullptr, nullptr},
1047 };
1048
1049 static const EnumPropertyItem position_mode_items[] = {
1050 {int(UVAlignPositionMode::Mean), "MEAN", 0, "Mean", "Align UVs along the mean position"},
1051 {int(UVAlignPositionMode::Min), "MIN", 0, "Minimum", "Align UVs along the minimum position"},
1052 {int(UVAlignPositionMode::Max), "MAX", 0, "Maximum", "Align UVs along the maximum position"},
1053 {0, nullptr, 0, nullptr, nullptr},
1054 };
1055
1056 /* identifiers */
1057 ot->name = "Align";
1058
1059 ot->description = "Aligns selected UV vertices on a line";
1060 ot->idname = "UV_OT_align";
1061 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1062
1063 /* API callbacks. */
1064 ot->exec = uv_align_exec;
1065 ot->poll = ED_operator_uvedit;
1066
1067 ot->poll_property = uv_align_poll_property;
1068
1069 /* properties */
1071 ot->srna, "axis", axis_items, UV_ALIGN_AUTO, "Axis", "Axis to align UV locations on");
1072 RNA_def_enum(ot->srna,
1073 "position_mode",
1074 position_mode_items,
1076 "Position Mode",
1077 "Method of calculating the alignment position");
1078}
1079
1081
1082/* -------------------------------------------------------------------- */
1085
1087{
1088 Scene *scene = CTX_data_scene(C);
1089 ViewLayer *view_layer = CTX_data_view_layer(C);
1091
1092 const float threshold = RNA_float_get(op->ptr, "threshold");
1093
1095 scene, view_layer, nullptr);
1096
1097 bool *changed = MEM_calloc_arrayN<bool>(objects.size(), __func__);
1098
1099 /* Maximum index of an objects[i]'s UVs in UV_arr.
1100 * It helps find which UV in *uv_map_arr belongs to which object. */
1101 uint *ob_uv_map_max_idx = MEM_calloc_arrayN<uint>(objects.size(), __func__);
1102
1103 /* Calculate max possible number of kdtree nodes. */
1104 int uv_maxlen = 0;
1105 for (Object *obedit : objects) {
1107
1108 if (em->bm->totvertsel == 0) {
1109 continue;
1110 }
1111
1112 uv_maxlen += em->bm->totloop;
1113 }
1114
1115 KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
1116
1117 blender::Vector<int> duplicates;
1118 blender::Vector<float *> uv_map_arr;
1119
1120 int uv_map_count = 0; /* Also used for *duplicates count. */
1121
1122 for (const int ob_index : objects.index_range()) {
1123 Object *obedit = objects[ob_index];
1125 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1126 BLI_kdtree_2d_insert(tree, uv_map_count, luv);
1127 duplicates.append(-1);
1128 uv_map_arr.append(luv);
1129 uv_map_count++;
1130 });
1131
1132 ob_uv_map_max_idx[ob_index] = uv_map_count - 1;
1133 }
1134
1135 BLI_kdtree_2d_balance(tree);
1136 int found_duplicates = BLI_kdtree_2d_calc_duplicates_fast(
1137 tree, threshold, false, duplicates.data());
1138
1139 if (found_duplicates > 0) {
1140 /* Calculate average uv for duplicates. */
1141 int *uv_duplicate_count = MEM_calloc_arrayN<int>(uv_map_count, __func__);
1142 for (int i = 0; i < uv_map_count; i++) {
1143 if (duplicates[i] == -1) { /* If doesn't reference another */
1144 uv_duplicate_count[i]++; /* self */
1145 continue;
1146 }
1147
1148 if (duplicates[i] != i) {
1149 /* If not self then accumulate uv for averaging.
1150 * Self uv is already present in accumulator */
1151 add_v2_v2(uv_map_arr[duplicates[i]], uv_map_arr[i]);
1152 }
1153 uv_duplicate_count[duplicates[i]]++;
1154 }
1155
1156 for (int i = 0; i < uv_map_count; i++) {
1157 if (uv_duplicate_count[i] < 2) {
1158 continue;
1159 }
1160
1161 mul_v2_fl(uv_map_arr[i], 1.0f / float(uv_duplicate_count[i]));
1162 }
1163 MEM_freeN(uv_duplicate_count);
1164
1165 /* Update duplicated uvs. */
1166 uint ob_index = 0;
1167 for (int i = 0; i < uv_map_count; i++) {
1168 /* Make sure we know which object owns the uv_map at this index.
1169 * Remember that in some cases the object will have no loop uv,
1170 * thus we need the while loop, and not simply an if check. */
1171 while (ob_uv_map_max_idx[ob_index] < i) {
1172 ob_index++;
1173 }
1174
1175 if (duplicates[i] == -1) {
1176 continue;
1177 }
1178
1179 copy_v2_v2(uv_map_arr[i], uv_map_arr[duplicates[i]]);
1180 changed[ob_index] = true;
1181 }
1182
1183 for (ob_index = 0; ob_index < objects.size(); ob_index++) {
1184 if (changed[ob_index]) {
1185 Object *obedit = objects[ob_index];
1186 uvedit_live_unwrap_update(sima, scene, obedit);
1187 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1189 }
1190 }
1191 }
1192
1193 BLI_kdtree_2d_free(tree);
1194 MEM_freeN(changed);
1195 MEM_freeN(ob_uv_map_max_idx);
1196
1197 return OPERATOR_FINISHED;
1198}
1199
1201{
1202 Scene *scene = CTX_data_scene(C);
1203 ViewLayer *view_layer = CTX_data_view_layer(C);
1205 const float threshold = RNA_float_get(op->ptr, "threshold");
1206
1208 scene, view_layer, nullptr);
1209
1210 /* Calculate max possible number of kdtree nodes. */
1211 int uv_maxlen = 0;
1212 for (Object *obedit : objects) {
1214 uv_maxlen += em->bm->totloop;
1215 }
1216
1217 KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
1218
1219 blender::Vector<float *> uv_map_arr;
1220
1221 int uv_map_count = 0;
1222
1223 /* Add visible non-selected uvs to tree */
1224 ED_uvedit_foreach_uv_multi(scene, objects, true, false, [&](float luv[2]) {
1225 BLI_kdtree_2d_insert(tree, uv_map_count, luv);
1226 uv_map_arr.append(luv);
1227 uv_map_count++;
1228 });
1229
1230 BLI_kdtree_2d_balance(tree);
1231
1232 /* For each selected uv, find duplicate non selected uv. */
1233 for (Object *obedit : objects) {
1234 bool changed = false;
1236 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1237 KDTreeNearest_2d nearest;
1238 const int i = BLI_kdtree_2d_find_nearest(tree, luv, &nearest);
1239
1240 if (i != -1 && nearest.dist < threshold) {
1241 copy_v2_v2(luv, uv_map_arr[i]);
1242 changed = true;
1243 }
1244 });
1245
1246 if (changed) {
1247 uvedit_live_unwrap_update(sima, scene, obedit);
1248 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1249 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1250 }
1251 }
1252
1253 BLI_kdtree_2d_free(tree);
1254
1255 return OPERATOR_FINISHED;
1256}
1257
1259{
1260 /* NOTE: The calculation for the center-point of loops belonging to a vertex will be skewed
1261 * if one UV coordinate holds more loops than the others. */
1262
1263 Scene *scene = CTX_data_scene(C);
1265 ViewLayer *view_layer = CTX_data_view_layer(C);
1267 scene, view_layer, nullptr);
1268
1269 /* Only use the squared distance, to avoid a square-root. */
1270 const float threshold_sq = math::square(RNA_float_get(op->ptr, "threshold"));
1271
1272 for (Object *obedit : objects) {
1274 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1275 BMVert *v;
1276 BMLoop *l;
1277 BMIter viter, liter;
1278
1279 /* The `changed` variable keeps track if any loops from the current object are merged. */
1281 uvs.reserve(32);
1282 bool changed = false;
1283
1284 BM_ITER_MESH (v, &viter, em->bm, BM_VERTS_OF_MESH) {
1285
1286 BLI_assert(uvs.size() == 0);
1287 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
1288 if (uvedit_uv_select_test(scene, em->bm, l, offsets)) {
1289 uvs.append(BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv));
1290 }
1291 }
1292 if (uvs.size() <= 1) {
1293 uvs.clear();
1294 continue;
1295 }
1296
1297 while (uvs.size() > 1) {
1298 const int uvs_num = uvs.size();
1299 float2 uv_average = {0.0f, 0.0f};
1300 for (const float *luv : uvs) {
1301 uv_average += float2(luv);
1302 }
1303 uv_average /= uvs_num;
1304
1305 /* Find the loop closest to the uv_average. This loop will be the base that all
1306 * other loop's distances are calculated from. */
1307
1308 float dist_best_sq = math::distance_squared(uv_average, float2(uvs[0]));
1309 float *uv_ref = uvs[0];
1310 int uv_ref_index = 0;
1311 for (int i = 1; i < uvs_num; i++) {
1312 const float dist_test_sq = math::distance_squared(uv_average, float2(uvs[i]));
1313 if (dist_test_sq < dist_best_sq) {
1314 dist_best_sq = dist_test_sq;
1315 uv_ref = uvs[i];
1316 uv_ref_index = i;
1317 }
1318 }
1319
1320 const int uvs_end = uvs_num - 1;
1321 std::swap(uvs[uv_ref_index], uvs[uvs_end]);
1322
1323 /* Move all the UVs within threshold to the end of the array. Sum of all UV coordinates
1324 * within threshold is initialized with `uv_ref` coordinate data since while loop
1325 * ends once it hits `uv_ref` UV. */
1326 float2 uv_merged_average = {uv_ref[0], uv_ref[1]};
1327 int i = 0;
1328 int uvs_num_merged = 1;
1329 while (uvs[i] != uv_ref && i < uvs_num - uvs_num_merged) {
1330 const float dist_test_sq = len_squared_v2v2(uv_ref, uvs[i]);
1331 if (dist_test_sq < threshold_sq) {
1332 uv_merged_average += float2(uvs[i]);
1333 std::swap(uvs[i], uvs[uvs_end - uvs_num_merged]);
1334 uvs_num_merged++;
1335 if (dist_test_sq != 0.0f) {
1336 changed = true;
1337 }
1338 }
1339 else {
1340 i++;
1341 }
1342 }
1343
1344 /* Recalculate `uv_average` so it only considers UV's that are being included in merge
1345 * operation. Then Shift all loops to that position. */
1346 if (uvs_num_merged > 1) {
1347 uv_merged_average /= uvs_num_merged;
1348
1349 for (int j = uvs_num - uvs_num_merged; j < uvs_num; j++) {
1350 copy_v2_v2(uvs[j], uv_merged_average);
1351 }
1352 }
1353
1354 uvs.resize(uvs_num - uvs_num_merged);
1355 }
1356 uvs.clear();
1357 }
1358 if (changed) {
1359 uvedit_live_unwrap_update(sima, scene, obedit);
1360 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1361 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1362 }
1363 }
1364
1365 return OPERATOR_FINISHED;
1366}
1367
1369{
1370 if (RNA_boolean_get(op->ptr, "use_unselected")) {
1372 }
1373 if (RNA_boolean_get(op->ptr, "use_shared_vertex")) {
1375 }
1376 return uv_remove_doubles_to_selected(C, op);
1377}
1378
1380{
1381 /* identifiers */
1382 ot->name = "Merge UVs by Distance";
1383 ot->description =
1384 "Selected UV vertices that are within a radius of each other are welded together";
1385 ot->idname = "UV_OT_remove_doubles";
1386 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1387
1388 /* API callbacks. */
1389 ot->exec = uv_remove_doubles_exec;
1390 ot->poll = ED_operator_uvedit;
1391
1392 RNA_def_float(ot->srna,
1393 "threshold",
1394 0.02f,
1395 0.0f,
1396 10.0f,
1397 "Merge Distance",
1398 "Maximum distance between welded vertices",
1399 0.0f,
1400 1.0f);
1401 RNA_def_boolean(ot->srna,
1402 "use_unselected",
1403 false,
1404 "Unselected",
1405 "Merge selected to other unselected vertices");
1407 ot->srna, "use_shared_vertex", false, "Shared Vertex", "Weld UVs based on shared vertices");
1408}
1409
1411
1412/* -------------------------------------------------------------------- */
1415
1417{
1418 uv_weld(C);
1419
1420 return OPERATOR_FINISHED;
1421}
1422
1424{
1425 /* identifiers */
1426 ot->name = "Weld";
1427 ot->description = "Weld selected UV vertices together";
1428 ot->idname = "UV_OT_weld";
1429 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1430
1431 /* API callbacks. */
1432 ot->exec = uv_weld_exec;
1433 ot->poll = ED_operator_uvedit;
1434}
1435
1437
1438/* -------------------------------------------------------------------- */
1441
1442static void uv_snap_to_pixel(float uvco[2], float w, float h)
1443{
1444 uvco[0] = roundf(uvco[0] * w) / w;
1445 uvco[1] = roundf(uvco[1] * h) / h;
1446}
1447
1449{
1450 int width = 0, height = 0;
1451
1452 ED_space_image_get_size(sima, &width, &height);
1453 uv_snap_to_pixel(sima->cursor, width, height);
1454}
1455
1457 Span<Object *> objects_edit,
1458 SpaceImage *sima)
1459{
1460 return ED_uvedit_center_multi(scene, objects_edit, sima->cursor, sima->around);
1461}
1462
1463static void uv_snap_cursor_to_origin(float uvco[2])
1464{
1465 uvco[0] = 0;
1466 uvco[1] = 0;
1467}
1468
1470{
1472
1473 bool changed = false;
1474
1475 switch (RNA_enum_get(op->ptr, "target")) {
1476 case 0:
1478 changed = true;
1479 break;
1480 case 1: {
1481 Scene *scene = CTX_data_scene(C);
1482 ViewLayer *view_layer = CTX_data_view_layer(C);
1483
1484 Vector<Object *> objects =
1486 scene, view_layer, nullptr);
1487 changed = uv_snap_cursor_to_selection(scene, objects, sima);
1488 break;
1489 }
1490 case 2:
1492 changed = true;
1493 break;
1494 }
1495
1496 if (!changed) {
1497 return OPERATOR_CANCELLED;
1498 }
1499
1501
1502 return OPERATOR_FINISHED;
1503}
1504
1506{
1507 static const EnumPropertyItem target_items[] = {
1508 {0, "PIXELS", 0, "Pixels", ""},
1509 {1, "SELECTED", 0, "Selected", ""},
1510 {2, "ORIGIN", 0, "Origin", ""},
1511 {0, nullptr, 0, nullptr, nullptr},
1512 };
1513
1514 /* identifiers */
1515 ot->name = "Snap Cursor";
1516 ot->description = "Snap cursor to target type";
1517 ot->idname = "UV_OT_snap_cursor";
1518 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1519
1520 /* API callbacks. */
1521 ot->exec = uv_snap_cursor_exec;
1522 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
1523
1524 /* properties */
1526 ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1527}
1528
1530
1531/* -------------------------------------------------------------------- */
1534
1535static bool uv_snap_uvs_to_cursor(Scene *scene, Object *obedit, const float cursor[2])
1536{
1538 bool changed = false;
1539
1540 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1541 copy_v2_v2(luv, cursor);
1542 changed = true;
1543 });
1544
1545 return changed;
1546}
1547
1548static bool uv_snap_uvs_offset(Scene *scene, Object *obedit, const float offset[2])
1549{
1551 bool changed = false;
1552
1553 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1554 add_v2_v2(luv, offset);
1555 changed = true;
1556 });
1557
1558 return changed;
1559}
1560
1562{
1564 BMesh *bm = em->bm;
1565 BMFace *f;
1566 BMLoop *l, *lsub;
1567 BMIter iter, liter, lsubiter;
1568 float *luv;
1569 bool changed = false;
1570 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1571
1572 /* Index every vert that has a selected UV using it, but only once so as to
1573 * get unique indices and to count how much to `malloc`. */
1574 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1575 if (uvedit_face_visible_test(scene, f)) {
1577 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1579 }
1580 }
1581 else {
1583 }
1584 }
1585
1586 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1587 if (BM_elem_flag_test(f, BM_ELEM_TAG)) { /* Face: visible. */
1588 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1589 if (BM_elem_flag_test(l, BM_ELEM_TAG)) { /* Loop: selected. */
1590 float uv[2] = {0.0f, 0.0f};
1591 int uv_tot = 0;
1592
1593 BM_ITER_ELEM (lsub, &lsubiter, l->v, BM_LOOPS_OF_VERT) {
1594 if (BM_elem_flag_test(lsub->f, BM_ELEM_TAG) && /* Face: visible. */
1595 !BM_elem_flag_test(lsub, BM_ELEM_TAG)) /* Loop: unselected. */
1596 {
1597 luv = BM_ELEM_CD_GET_FLOAT_P(lsub, offsets.uv);
1598 add_v2_v2(uv, luv);
1599 uv_tot++;
1600 }
1601 }
1602
1603 if (uv_tot) {
1604 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
1605 mul_v2_v2fl(luv, uv, 1.0f / float(uv_tot));
1606 changed = true;
1607 }
1608 }
1609 }
1610 }
1611 }
1612
1613 return changed;
1614}
1615
1616static bool uv_snap_uvs_to_pixels(SpaceImage *sima, Scene *scene, Object *obedit)
1617{
1619 int width = 0, height = 0;
1620 float w, h;
1621 bool changed = false;
1622
1623 ED_space_image_get_size(sima, &width, &height);
1624 w = float(width);
1625 h = float(height);
1626
1627 ED_uvedit_foreach_uv(scene, em->bm, true, true, [&](float luv[2]) {
1628 uv_snap_to_pixel(luv, w, h);
1629 changed = true;
1630 });
1631
1632 return changed;
1633}
1634
1636{
1637 Scene *scene = CTX_data_scene(C);
1638 ViewLayer *view_layer = CTX_data_view_layer(C);
1640 const int target = RNA_enum_get(op->ptr, "target");
1641 float offset[2] = {0};
1642
1644 scene, view_layer, nullptr);
1645
1646 if (target == 2) {
1647 float center[2];
1648 if (!ED_uvedit_center_multi(scene, objects, center, sima->around)) {
1649 return OPERATOR_CANCELLED;
1650 }
1651 sub_v2_v2v2(offset, sima->cursor, center);
1652 }
1653
1654 bool changed_multi = false;
1655 for (Object *obedit : objects) {
1657
1658 if (em->bm->totvertsel == 0) {
1659 continue;
1660 }
1661
1662 bool changed = false;
1663 switch (target) {
1664 case 0:
1665 changed = uv_snap_uvs_to_pixels(sima, scene, obedit);
1666 break;
1667 case 1:
1668 changed = uv_snap_uvs_to_cursor(scene, obedit, sima->cursor);
1669 break;
1670 case 2:
1671 changed = uv_snap_uvs_offset(scene, obedit, offset);
1672 break;
1673 case 3:
1674 changed = uv_snap_uvs_to_adjacent_unselected(scene, obedit);
1675 break;
1676 }
1677
1678 if (changed) {
1679 changed_multi = true;
1680 uvedit_live_unwrap_update(sima, scene, obedit);
1681 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1682 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1683 }
1684 }
1685
1686 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1687}
1688
1690{
1691 static const EnumPropertyItem target_items[] = {
1692 {0, "PIXELS", 0, "Pixels", ""},
1693 {1, "CURSOR", 0, "Cursor", ""},
1694 {2, "CURSOR_OFFSET", 0, "Cursor (Offset)", ""},
1695 {3, "ADJACENT_UNSELECTED", 0, "Adjacent Unselected", ""},
1696 {0, nullptr, 0, nullptr, nullptr},
1697 };
1698
1699 /* identifiers */
1700 ot->name = "Snap Selection";
1701 ot->description = "Snap selected UV vertices to target type";
1702 ot->idname = "UV_OT_snap_selected";
1703 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1704
1705 /* API callbacks. */
1706 ot->exec = uv_snap_selection_exec;
1708
1709 /* properties */
1711 ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1712}
1713
1715
1716/* -------------------------------------------------------------------- */
1719
1721{
1722 Scene *scene = CTX_data_scene(C);
1723 ViewLayer *view_layer = CTX_data_view_layer(C);
1724 BMFace *efa;
1725 BMLoop *l;
1726 BMIter iter, liter;
1727 const bool clear = RNA_boolean_get(op->ptr, "clear");
1728 const bool invert = RNA_boolean_get(op->ptr, "invert");
1729
1731 scene, view_layer, nullptr);
1732
1733 for (Object *obedit : objects) {
1735
1736 bool changed = false;
1737
1738 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1739 if (em->bm->totvertsel == 0) {
1740 continue;
1741 }
1742
1743 if (clear && !BM_uv_map_attr_pin_exists(em->bm, active_uv_name)) {
1744 continue;
1745 }
1746
1747 BM_uv_map_attr_pin_ensure_named(em->bm, active_uv_name);
1748 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
1749
1750 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1751 if (!uvedit_face_visible_test(scene, efa)) {
1752 continue;
1753 }
1754
1755 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1756
1757 if (uvedit_uv_select_test(scene, em->bm, l, offsets)) {
1758 changed = true;
1759 if (invert) {
1760 BM_ELEM_CD_SET_BOOL(l, offsets.pin, !BM_ELEM_CD_GET_BOOL(l, offsets.pin));
1761 }
1762 else {
1763 BM_ELEM_CD_SET_BOOL(l, offsets.pin, !clear);
1764 }
1765 }
1766 }
1767 }
1768
1769 if (changed) {
1770 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1771 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SYNC_TO_EVAL);
1772 }
1773 }
1774
1775 return OPERATOR_FINISHED;
1776}
1777
1779{
1780 PropertyRNA *prop;
1781
1782 /* identifiers */
1783 ot->name = "Pin";
1784 ot->description =
1785 "Set/clear selected UV vertices as anchored between multiple unwrap operations";
1786 ot->idname = "UV_OT_pin";
1787 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1788
1789 /* API callbacks. */
1790 ot->exec = uv_pin_exec;
1791 ot->poll = ED_operator_uvedit;
1792
1793 /* properties */
1794 prop = RNA_def_boolean(
1795 ot->srna, "clear", false, "Clear", "Clear pinning for the selection instead of setting it");
1797 prop = RNA_def_boolean(ot->srna,
1798 "invert",
1799 false,
1800 "Invert",
1801 "Invert pinning for the selection instead of setting it");
1803}
1804
1806
1807/* -------------------------------------------------------------------- */
1810
1811/* check if we are selected or unselected based on 'bool_test' arg,
1812 * needed for select swap support */
1813#define UV_VERT_SEL_TEST(ts, bm, l, bool_test) \
1814 (uvedit_vert_select_get_no_sync(ts, bm, l) == bool_test)
1815
1816#define UV_EDGE_SEL_TEST(ts, bm, l, bool_test) \
1817 (uvedit_edge_select_get_no_sync(ts, bm, l) == bool_test)
1818
1819/* is every UV vert selected or unselected depending on bool_test */
1821 const BMesh *bm,
1822 BMFace *f,
1823 bool select_test)
1824{
1825 BMLoop *l_iter;
1826 BMLoop *l_first;
1827
1828 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1829 do {
1830 if (!UV_EDGE_SEL_TEST(ts, bm, l_iter, select_test)) {
1831 return false;
1832 }
1833 } while ((l_iter = l_iter->next) != l_first);
1834
1835 return true;
1836}
1837
1838static bool uv_mesh_hide_sync_select(const ToolSettings *ts, Object *ob, BMEditMesh *em, bool swap)
1839{
1840 const bool select_to_hide = !swap;
1841 BMesh *bm = em->bm;
1842 bool changed = false;
1843
1844 if (bm->uv_select_sync_valid == false || ED_uvedit_sync_uvselect_ignore(ts)) {
1845 /* Simple case, no need to synchronize UV's, forward to mesh hide. */
1846 changed = EDBM_mesh_hide(em, swap);
1847 }
1848 else {
1849 /* For vertices & edges hiding faces immediately causes a feedback loop,
1850 * where hiding doesn't work predictably as values are being both read and written to.
1851 * Perform two passes, use tagging. */
1852
1853 /* Vertex and edge modes use almost the same logic. */
1855 BMIter iter;
1857
1858 if (em->selectmode & SCE_SELECT_VERTEX) {
1859 BMFace *f;
1860 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
1862 continue;
1863 }
1864 BMLoop *l_iter, *l_first;
1865 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1866 do {
1867 if ((BM_elem_flag_test_bool(l_iter->v, BM_ELEM_SELECT) == select_to_hide) &&
1868 (BM_elem_flag_test_bool(l_iter, BM_ELEM_SELECT_UV) == select_to_hide))
1869 {
1871 changed = true;
1872 break;
1873 }
1874 } while ((l_iter = l_iter->next) != l_first);
1875 }
1876 }
1877 else {
1879 BMFace *f;
1880 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
1882 continue;
1883 }
1884 BMLoop *l_iter, *l_first;
1885 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1886 do {
1887 if ((BM_elem_flag_test_bool(l_iter->e, BM_ELEM_SELECT) == select_to_hide) &&
1888 (BM_elem_flag_test_bool(l_iter, BM_ELEM_SELECT_UV_EDGE) == select_to_hide))
1889 {
1891 changed = true;
1892 break;
1893 }
1894 } while ((l_iter = l_iter->next) != l_first);
1895 }
1896 }
1897
1898 if (changed) {
1899 BMFace *f;
1900 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
1902 BM_elem_hide_set(bm, f, true);
1903 }
1904 }
1905 if (swap) {
1906 /* Without re-selecting, the faces vertices are de-selected when hiding adjacent faces.
1907 *
1908 * TODO(@ideasman42): consider a more elegant solution of ensuring
1909 * faces at the boundaries don't get their vertices de-selected.
1910 * This is low-priority as it's no a bottleneck. */
1911 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
1912 if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
1913 BM_face_select_set(bm, f, true);
1914 }
1915 }
1916 }
1917 }
1918 }
1919 else {
1921 BMIter iter;
1922 BMFace *f;
1923 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
1925 continue;
1926 }
1927 if (BM_elem_flag_test_bool(f, BM_ELEM_SELECT_UV) == select_to_hide) {
1928 BM_elem_hide_set(bm, f, true);
1929 changed = true;
1930 }
1931 }
1932 }
1933
1934 if (changed) {
1935 if (swap) {
1937 }
1938 else {
1940 }
1941 /* Clearing is OK even when hiding unselected
1942 * as the remaining geometry is entirely selected. */
1944 }
1945 }
1946
1947 if (changed) {
1948 Mesh *mesh = static_cast<Mesh *>(ob->data);
1950 params.calc_looptris = true;
1951 params.calc_normals = false;
1952 params.is_destructive = false;
1953 EDBM_update(mesh, &params);
1954 }
1955
1956 return changed;
1957}
1958
1960{
1961 ViewLayer *view_layer = CTX_data_view_layer(C);
1962 Scene *scene = CTX_data_scene(C);
1963 const ToolSettings *ts = scene->toolsettings;
1964 const bool swap = RNA_boolean_get(op->ptr, "unselected");
1965 const bool use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1966 const bool use_select_linked = ED_uvedit_select_island_check(ts);
1967
1969 scene, view_layer, nullptr);
1970
1971 for (Object *ob : objects) {
1973 BMFace *efa;
1974 BMLoop *l;
1975 BMIter iter, liter;
1976
1977 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
1978 uv_mesh_hide_sync_select(ts, ob, em, swap);
1979 continue;
1980 }
1981
1982 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1983 int hide = 0;
1984
1985 if (!uvedit_face_visible_test(scene, efa)) {
1986 continue;
1987 }
1988
1989 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1990
1991 if (UV_VERT_SEL_TEST(ts, em->bm, l, !swap) || UV_EDGE_SEL_TEST(ts, em->bm, l, !swap)) {
1992 hide = 1;
1993 break;
1994 }
1995 }
1996
1997 if (hide) {
1998 if (use_face_center) {
1999 if (em->selectmode == SCE_SELECT_FACE) {
2000 /* Deselect BMesh face if UV face is (de)selected depending on #swap. */
2001 if (bm_face_is_all_uv_sel(ts, em->bm, efa, !swap)) {
2002 BM_face_select_set(em->bm, efa, false);
2003 }
2004 uvedit_face_select_disable(scene, em->bm, efa);
2005 }
2006 else {
2007 if (bm_face_is_all_uv_sel(ts, em->bm, efa, true) == !swap) {
2008 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2009 /* For both cases rely on edge sel tests, since all vert sel tests are invalid in
2010 * case of sticky selections. */
2011 if (UV_EDGE_SEL_TEST(ts, em->bm, l, !swap) && (em->selectmode == SCE_SELECT_EDGE))
2012 {
2013 BM_edge_select_set(em->bm, l->e, false);
2014 }
2015 else if (UV_EDGE_SEL_TEST(ts, em->bm, l, !swap) &&
2017 {
2018 BM_vert_select_set(em->bm, l->v, false);
2019 }
2020 }
2021 }
2022 if (!swap) {
2023 uvedit_face_select_disable(scene, em->bm, efa);
2024 }
2025 }
2026 }
2027 else if (em->selectmode == SCE_SELECT_FACE) {
2028 /* Deselect BMesh face depending on the type of UV selectmode and the type of UV element
2029 * being considered. */
2030 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2031 if (UV_EDGE_SEL_TEST(ts, em->bm, l, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) {
2032 BM_face_select_set(em->bm, efa, false);
2033 break;
2034 }
2035 if (UV_VERT_SEL_TEST(ts, em->bm, l, !swap) && (ts->uv_selectmode == UV_SELECT_VERT)) {
2036 BM_face_select_set(em->bm, efa, false);
2037 break;
2038 }
2039 if (use_select_linked) {
2040 BM_face_select_set(em->bm, efa, false);
2041 break;
2042 }
2043 }
2044 uvedit_face_select_disable(scene, em->bm, efa);
2045 }
2046 else {
2047 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2048 if (UV_EDGE_SEL_TEST(ts, em->bm, l, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) {
2049 if (em->selectmode == SCE_SELECT_EDGE) {
2050 BM_edge_select_set(em->bm, l->e, false);
2051 }
2052 else {
2053 BM_vert_select_set(em->bm, l->v, false);
2054 BM_vert_select_set(em->bm, l->next->v, false);
2055 }
2056 }
2057 else if (UV_VERT_SEL_TEST(ts, em->bm, l, !swap) &&
2059 {
2060 if (em->selectmode == SCE_SELECT_EDGE) {
2061 BM_edge_select_set(em->bm, l->e, false);
2062 }
2063 else {
2064 BM_vert_select_set(em->bm, l->v, false);
2065 }
2066 }
2067 }
2068 if (!swap) {
2069 uvedit_face_select_disable(scene, em->bm, efa);
2070 }
2071 }
2072 }
2073 }
2074
2075 /* Flush editmesh selections to ensure valid selection states. */
2076 if (em->selectmode != SCE_SELECT_FACE) {
2077 /* NOTE: Make sure correct flags are used. Previously this was done by passing
2078 * (SCE_SELECT_VERTEX | SCE_SELECT_EDGE), which doesn't work now that we support proper UV
2079 * edge selection. */
2080
2082 }
2083
2085
2086 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
2088 }
2089
2090 return OPERATOR_FINISHED;
2091}
2092
2093#undef UV_VERT_SEL_TEST
2094#undef UV_EDGE_SEL_TEST
2095
2097{
2098 /* identifiers */
2099 ot->name = "Hide Selected";
2100 ot->description = "Hide (un)selected UV vertices";
2101 ot->idname = "UV_OT_hide";
2102 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2103
2104 /* API callbacks. */
2105 ot->exec = uv_hide_exec;
2106 ot->poll = ED_operator_uvedit;
2107
2108 /* props */
2110 ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
2111}
2112
2114
2115/* -------------------------------------------------------------------- */
2118
2120{
2121 ViewLayer *view_layer = CTX_data_view_layer(C);
2122 Scene *scene = CTX_data_scene(C);
2123 const ToolSettings *ts = scene->toolsettings;
2124
2125 const bool use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
2126 const bool select = RNA_boolean_get(op->ptr, "select");
2127
2129 scene, view_layer, nullptr);
2130
2131 for (Object *ob : objects) {
2133 BMFace *efa;
2134 BMLoop *l;
2135 BMIter iter, liter;
2136
2137 /* NOTE: Selecting faces is delayed so that it doesn't select verts/edges and confuse certain
2138 * UV selection checks.
2139 * This creates a temporary state which breaks certain UV selection functions that do face
2140 * visibility checks internally. Current implementation handles each case separately. */
2141
2142 /* call the mesh function if we are in mesh sync sel */
2143 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2144 if (EDBM_mesh_reveal(em, select)) {
2145 Mesh *mesh = static_cast<Mesh *>(ob->data);
2147 params.calc_looptris = true;
2148 params.calc_normals = false;
2149 params.is_destructive = false;
2150 EDBM_update(mesh, &params);
2151 }
2152 continue;
2153 }
2154
2155 /* NOTE(@sidd017): Supporting selections in all cases is quite difficult considering there are
2156 * at least 12 cases to look into (3 mesh select-modes + 4 uv select-modes + sticky modes).
2157 * For now we select all UV faces as sticky disabled to ensure proper UV selection states (vert
2158 * + edge flags) */
2159 if (use_face_center) {
2160 if (em->selectmode == SCE_SELECT_FACE) {
2161 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2164 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2167 }
2169 // BM_face_select_set(em->bm, efa, true);
2171 }
2172 }
2173 }
2174 else {
2175 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2178 int totsel = 0;
2179 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2180 if (em->selectmode == SCE_SELECT_VERTEX) {
2181 totsel += BM_elem_flag_test(l->v, BM_ELEM_SELECT);
2182 }
2183 else if (em->selectmode == SCE_SELECT_EDGE) {
2184 totsel += BM_elem_flag_test(l->e, BM_ELEM_SELECT);
2185 }
2186 }
2187
2188 if (!totsel) {
2189 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2190
2193 }
2194 }
2196 // BM_face_select_set(em->bm, efa, true);
2198 }
2199 }
2200 }
2201 }
2202 else if (em->selectmode == SCE_SELECT_FACE) {
2203 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2206 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2209 }
2211 // BM_face_select_set(em->bm, efa, true);
2213 }
2214 }
2215 }
2216 else {
2217 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2220 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2223 }
2225 // BM_face_select_set(em->bm, efa, true);
2227 }
2228 }
2229 }
2230
2231 /* re-select tagged faces */
2233
2234 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_SELECT);
2236 }
2237
2238 return OPERATOR_FINISHED;
2239}
2240
2242{
2243 /* identifiers */
2244 ot->name = "Reveal Hidden";
2245 ot->description = "Reveal all hidden UV vertices";
2246 ot->idname = "UV_OT_reveal";
2247 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2248
2249 /* API callbacks. */
2250 ot->exec = uv_reveal_exec;
2251 ot->poll = ED_operator_uvedit;
2252
2253 RNA_def_boolean(ot->srna, "select", true, "Select", "");
2254}
2255
2257
2258/* -------------------------------------------------------------------- */
2261
2263{
2265
2266 if (!sima) {
2267 return OPERATOR_CANCELLED;
2268 }
2269
2270 RNA_float_get_array(op->ptr, "location", sima->cursor);
2271
2272 {
2273 wmMsgBus *mbus = CTX_wm_message_bus(C);
2274 bScreen *screen = CTX_wm_screen(C);
2275 WM_msg_publish_rna_prop(mbus, &screen->id, sima, SpaceImageEditor, cursor_location);
2276 }
2277
2279
2280 /* Use pass-through to allow click-drag to transform the cursor. */
2282}
2283
2285{
2286 ARegion *region = CTX_wm_region(C);
2287 float location[2];
2288
2289 if (region->regiontype == RGN_TYPE_WINDOW) {
2291 if (sima && ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) {
2292 return OPERATOR_PASS_THROUGH;
2293 }
2294 }
2295
2297 &region->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
2298 RNA_float_set_array(op->ptr, "location", location);
2299
2300 return uv_set_2d_cursor_exec(C, op);
2301}
2302
2304{
2305 /* identifiers */
2306 ot->name = "Set 2D Cursor";
2307 ot->description = "Set 2D cursor location";
2308 ot->idname = "UV_OT_cursor_set";
2309
2310 /* API callbacks. */
2311 ot->exec = uv_set_2d_cursor_exec;
2312 ot->invoke = uv_set_2d_cursor_invoke;
2314
2315 /* properties */
2317 "location",
2318 2,
2319 nullptr,
2320 -FLT_MAX,
2321 FLT_MAX,
2322 "Location",
2323 "Cursor location in normalized (0.0 to 1.0) coordinates",
2324 -10.0f,
2325 10.0f);
2326}
2327
2329
2330/* -------------------------------------------------------------------- */
2333
2335{
2336 Scene *scene = CTX_data_scene(C);
2337 ViewLayer *view_layer = CTX_data_view_layer(C);
2338 const bool mark_seams = RNA_boolean_get(op->ptr, "mark_seams");
2339 const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
2340 bool changed_multi = false;
2341
2343 scene, view_layer, nullptr);
2344
2345 for (Object *ob : objects) {
2346 Mesh *mesh = (Mesh *)ob->data;
2347 BMEditMesh *em = mesh->runtime->edit_mesh.get();
2348 BMesh *bm = em->bm;
2349 BMIter iter;
2350
2351 if (!EDBM_uv_check(em)) {
2352 continue;
2353 }
2354
2355 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
2356 bool changed = false;
2357
2358 BMFace *f;
2359 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
2360 if (!uvedit_face_visible_test(scene, f)) {
2361 continue;
2362 }
2363
2364 BMLoop *l_iter;
2365 BMLoop *l_first;
2366
2367 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
2368 do {
2369 if (l_iter == l_iter->radial_next) {
2370 continue;
2371 }
2372 if (!uvedit_edge_select_test(scene, em->bm, l_iter, offsets)) {
2373 continue;
2374 }
2375
2376 bool mark = false;
2377 BMLoop *l_other = l_iter->radial_next;
2378 do {
2379 if (!BM_loop_uv_share_edge_check(l_iter, l_other, offsets.uv)) {
2380 mark = true;
2381 break;
2382 }
2383 } while ((l_other = l_other->radial_next) != l_iter);
2384
2385 if (mark) {
2386 if (mark_seams) {
2388 }
2389 if (mark_sharp) {
2391 }
2392 changed = true;
2393 }
2394 } while ((l_iter = l_iter->next) != l_first);
2395 }
2396
2397 if (changed) {
2398 changed_multi = true;
2399 DEG_id_tag_update(&mesh->id, 0);
2401 }
2402 }
2403
2404 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2405}
2406
2408{
2409 /* identifiers */
2410 ot->name = "Seams from Islands";
2411 ot->description = "Set mesh seams according to island setup in the UV editor";
2412 ot->idname = "UV_OT_seams_from_islands";
2413
2414 /* flags */
2415 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2416
2417 /* API callbacks. */
2419 ot->poll = ED_operator_uvedit;
2420
2421 RNA_def_boolean(ot->srna, "mark_seams", true, "Mark Seams", "Mark boundary edges as seams");
2422 RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark boundary edges as sharp");
2423}
2424
2426
2427/* -------------------------------------------------------------------- */
2430
2432{
2433 Scene *scene = CTX_data_scene(C);
2434 ViewLayer *view_layer = CTX_data_view_layer(C);
2435 const ToolSettings *ts = scene->toolsettings;
2436
2437 BMFace *efa;
2438 BMLoop *loop;
2439 BMIter iter, liter;
2440
2441 const bool flag_set = !RNA_boolean_get(op->ptr, "clear");
2442 const bool synced_selection = (ts->uv_flag & UV_FLAG_SELECT_SYNC) != 0;
2443
2445 scene, view_layer, nullptr);
2446
2447 bool changed = false;
2448
2449 for (Object *ob : objects) {
2450 Mesh *mesh = (Mesh *)ob->data;
2451 BMEditMesh *em = mesh->runtime->edit_mesh.get();
2452 BMesh *bm = em->bm;
2453
2454 if (synced_selection && (bm->totedgesel == 0)) {
2455 continue;
2456 }
2457
2458 const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
2459
2460 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2461 if (uvedit_face_visible_test(scene, efa)) {
2462 BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) {
2463 if (uvedit_edge_select_test(scene, bm, loop, offsets)) {
2464 BM_elem_flag_set(loop->e, BM_ELEM_SEAM, flag_set);
2465 changed = true;
2466 }
2467 }
2468 }
2469 }
2470
2471 if (changed) {
2472 DEG_id_tag_update(&mesh->id, 0);
2474 }
2475 }
2476
2477 if (changed) {
2478 ED_uvedit_live_unwrap(scene, objects);
2479 }
2480
2481 return OPERATOR_FINISHED;
2482}
2483
2485{
2486 uiPopupMenu *pup;
2487 uiLayout *layout;
2488
2489 if (RNA_struct_property_is_set(op->ptr, "clear")) {
2490 return uv_mark_seam_exec(C, op);
2491 }
2492
2493 pup = UI_popup_menu_begin(C, IFACE_("Edges"), ICON_NONE);
2494 layout = UI_popup_menu_layout(pup);
2495
2497 PointerRNA op_ptr = layout->op(
2498 op->type->idname, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Mark Seam"), ICON_NONE);
2499 RNA_boolean_set(&op_ptr, "clear", false);
2500 op_ptr = layout->op(
2501 op->type->idname, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Clear Seam"), ICON_NONE);
2502 RNA_boolean_set(&op_ptr, "clear", true);
2503
2504 UI_popup_menu_end(C, pup);
2505
2506 return OPERATOR_INTERFACE;
2507}
2508
2510{
2511 /* identifiers */
2512 ot->name = "Mark Seam";
2513 ot->description = "Mark selected UV edges as seams";
2514 ot->idname = "UV_OT_mark_seam";
2515
2516 /* flags */
2517 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2518
2519 /* API callbacks. */
2520 ot->exec = uv_mark_seam_exec;
2521 ot->invoke = uv_mark_seam_invoke;
2522 ot->poll = ED_operator_uvedit;
2523
2524 RNA_def_boolean(ot->srna, "clear", false, "Clear Seams", "Clear instead of marking seams");
2525}
2526
2528 const Scene *scene, BMesh *bm, int direction, int precision, int *r_double_warn)
2529{
2530 *r_double_warn = 0;
2531 const float precision_scale = powf(10.0f, precision);
2532 /* TODO: replace mirror look-ups with #EditMeshSymmetryHelper. */
2533 Map<float3, BMVert *> mirror_gt, mirror_lt;
2535
2536 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
2537 BLI_assert(offsets.uv != -1);
2538 UNUSED_VARS_NDEBUG(offsets);
2539 BMVert *v;
2540 BMIter iter;
2541
2542 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
2543 float3 pos = math::round(float3(v->co) * precision_scale);
2544 if (pos.x >= 0.0f) {
2545 if (!mirror_gt.add_overwrite(pos, v)) {
2546 (*r_double_warn)++;
2547 }
2548 }
2549 if (pos.x <= 0.0f) {
2550 if (!mirror_lt.add_overwrite(pos, v)) {
2551 (*r_double_warn)++;
2552 }
2553 }
2554 }
2555
2556 for (const auto &[pos, v] : mirror_gt.items()) {
2557 float3 mirror_pos = pos;
2558 mirror_pos[0] = -mirror_pos[0];
2559 BMVert *v_mirror = mirror_lt.lookup_default(mirror_pos, nullptr);
2560 if (v_mirror) {
2561 vmap.add(v, v_mirror);
2562 }
2563 }
2564 for (const auto &[pos, v] : mirror_lt.items()) {
2565 float3 mirror_pos = pos;
2566 mirror_pos[0] = -mirror_pos[0];
2567 BMVert *v_mirror = mirror_gt.lookup_default(mirror_pos, nullptr);
2568 if (v_mirror) {
2569 vmap.add(v, v_mirror);
2570 }
2571 }
2572
2573 Map<Array<BMVert *>, BMFace *> sorted_verts_to_face;
2574 /* Maps faces to their corresponding mirrored face. */
2575 Map<BMFace *, BMFace *> face_map;
2576
2577 BMFace *f;
2578 BMIter iter_face;
2579 BM_ITER_MESH (f, &iter_face, bm, BM_FACES_OF_MESH) {
2580 Array<BMVert *> sorted_verts(f->len);
2581 bool valid = true;
2582 int loop_index = 0;
2583 BMLoop *l;
2584 BMIter liter;
2585 BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, loop_index) {
2586 if (!vmap.contains(l->v)) {
2587 valid = false;
2588 break;
2589 }
2590 sorted_verts[loop_index] = l->v;
2591 }
2592 if (valid) {
2593 std::sort(sorted_verts.begin(), sorted_verts.end());
2594 sorted_verts_to_face.add(std::move(sorted_verts), f);
2595 }
2596 }
2597
2598 for (const auto &[sorted_verts, f_dst] : sorted_verts_to_face.items()) {
2599 Array<BMVert *> mirror_verts(sorted_verts.size());
2600 for (int index = 0; index < sorted_verts.size(); index++) {
2601 mirror_verts[index] = vmap.lookup_default(sorted_verts[index], nullptr);
2602 }
2603 std::sort(mirror_verts.begin(), mirror_verts.end());
2604 BMFace *f_src = sorted_verts_to_face.lookup_default(mirror_verts, nullptr);
2605 if (f_src) {
2606 if (f_src != f_dst) {
2607 face_map.add(f_dst, f_src);
2608 }
2609 }
2610 }
2611
2612 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
2613
2614 bool changed = false;
2615 for (const auto &[f_dst, f_src] : face_map.items()) {
2616
2617 /* Skip unless both faces have all their UVs selected. */
2618 if (!uvedit_face_select_test(scene, bm, f_dst) || !uvedit_face_select_test(scene, bm, f_src)) {
2619 continue;
2620 }
2621
2622 {
2623 float f_dst_center[3];
2624 BM_face_calc_center_median(f_dst, f_dst_center);
2625 if (direction ? (f_dst_center[0] > 0.0f) : (f_dst_center[0] < 0.0f)) {
2626 continue;
2627 }
2628 }
2629
2630 BMIter liter;
2631 BMLoop *l_dst;
2632
2633 BM_ITER_ELEM (l_dst, &liter, f_dst, BM_LOOPS_OF_FACE) {
2634 BMVert *v_src = vmap.lookup_default(l_dst->v, nullptr);
2635 if (!v_src) {
2636 continue;
2637 }
2638
2639 BMLoop *l_src = BM_face_vert_share_loop(f_src, v_src);
2640 if (!l_src) {
2641 continue;
2642 }
2643 const float *uv_src = BM_ELEM_CD_GET_FLOAT_P(l_src, cd_loop_uv_offset);
2644 float *uv_dst = BM_ELEM_CD_GET_FLOAT_P(l_dst, cd_loop_uv_offset);
2645
2646 uv_dst[0] = -(uv_src[0] - 0.5f) + 0.5f;
2647 uv_dst[1] = uv_src[1];
2648 changed = true;
2649 }
2650 }
2651
2652 return changed;
2653}
2654
2656{
2657 Scene *scene = CTX_data_scene(C);
2658 ViewLayer *view_layer = CTX_data_view_layer(C);
2660 scene, view_layer, nullptr);
2661 const int direction = RNA_enum_get(op->ptr, "direction");
2662 const int precision = RNA_int_get(op->ptr, "precision");
2663
2664 int total_duplicates = 0;
2665 int meshes_with_duplicates = 0;
2666
2667 for (Object *obedit : objects) {
2669
2670 int double_warn = 0;
2671
2672 bool changed = uv_copy_mirrored_faces(scene, em->bm, direction, precision, &double_warn);
2673
2674 if (double_warn) {
2675 total_duplicates += double_warn;
2676 meshes_with_duplicates++;
2677 }
2678
2679 if (changed) {
2680 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
2681 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
2682 }
2683 }
2684
2685 if (total_duplicates) {
2686 BKE_reportf(op->reports,
2688 "%d duplicates found in %d mesh(es), mirror may be incomplete",
2689 total_duplicates,
2690 meshes_with_duplicates);
2691 }
2692
2693 return OPERATOR_FINISHED;
2694}
2696{
2697 static const EnumPropertyItem direction_items[] = {
2698 {0, "POSITIVE", 0, "Positive", ""},
2699 {1, "NEGATIVE", 0, "Negative", ""},
2700 {0, nullptr, 0, nullptr, nullptr},
2701 };
2702
2703 ot->name = "Copy Mirrored UV Coords";
2704 ot->description = "Copy mirror UV coordinates on the X axis based on a mirrored mesh";
2705 ot->idname = "UV_OT_copy_mirrored_faces";
2706
2708 ot->poll = ED_operator_editmesh;
2709
2710 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2711
2712 RNA_def_enum(ot->srna, "direction", direction_items, 0, "Axis Direction", "");
2713 RNA_def_int(ot->srna,
2714 "precision",
2715 3,
2716 1,
2717 16,
2718 "Precision",
2719 "Tolerance for finding vertex duplicates",
2720 1,
2721 16);
2722}
2723
2725
2726/* -------------------------------------------------------------------- */
2729
2731{
2732 /* `uvedit_select.cc` */
2750
2753
2756
2761
2767
2778
2783
2787}
2788
2790{
2792 wmOperatorTypeMacro *otmacro;
2793
2794 ot = WM_operatortype_append_macro("UV_OT_rip_move",
2795 "UV Rip Move",
2796 "Unstitch UVs and move the result",
2798 WM_operatortype_macro_define(ot, "UV_OT_rip");
2799 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
2800 RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
2801 RNA_boolean_set(otmacro->ptr, "mirror", false);
2802}
2803
2805{
2806 wmKeyMap *keymap;
2807
2808 keymap = WM_keymap_ensure(keyconf, "UV Editor", SPACE_EMPTY, RGN_TYPE_WINDOW);
2809 keymap->poll = ED_operator_uvedit;
2810}
2811
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.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
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
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_WARNING
Definition BKE_report.hh:38
#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 UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define STREQ(a, b)
#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:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ CD_PROP_FLOAT2
@ IMA_SRC_TILED
Object is a sort of wrapper for general info.
@ OB_MESH
@ UV_SELECT_VERT
@ UV_SELECT_FACE
@ UV_SELECT_EDGE
@ UV_FLAG_SELECT_SYNC
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ RGN_TYPE_WINDOW
@ SI_LIVE_UNWRAP
@ SPACE_EMPTY
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ V3D_AROUND_CENTER_MEDIAN
@ 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_flag_disable_all(BMEditMesh *em, char hflag)
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
bool EDBM_uvselect_clear(BMEditMesh *em)
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 EDBM_selectmode_flush(BMEditMesh *em)
bool ED_operator_uvedit_space_image(bContext *C)
bool ED_operator_editmesh(bContext *C)
bool ED_operator_uvedit(bContext *C)
bool uvedit_edge_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit, struct wmWindow *win_modal)
bool ED_uvedit_sync_uvselect_ignore(const ToolSettings *ts)
void ED_uvedit_live_unwrap_re_solve()
bool uvedit_uv_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
bool ED_uvedit_select_island_check(const ToolSettings *ts)
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)
void uvedit_face_select_disable(const Scene *scene, BMesh *bm, BMFace *efa)
bool uvedit_face_select_test(const Scene *scene, const BMesh *bm, const BMFace *efa)
#define X
#define Y
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#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 UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SPACE_IMAGE
Definition WM_types.hh:522
#define ND_SELECT
Definition WM_types.hh:508
#define NC_SPACE
Definition WM_types.hh:392
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_ELEM_SELECT_UV_EDGE
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_SELECT_UV
@ BM_ELEM_TAG
#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_test_bool(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
bool BM_uv_map_attr_pin_exists(const BMesh *bm, const StringRef uv_map_name)
void BM_uv_map_attr_pin_ensure_named(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
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
BMesh * bm
void BM_mesh_select_mode_flush(BMesh *bm)
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_select_history_validate(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_elem_hflag_enable_test(BMesh *bm, const char htype, const char hflag, const bool respecthide, const bool overwrite, const char hflag_test)
#define BM_elem_hide_set(bm, ele, hide)
#define BM_FACE
ATTR_WARN_UNUSED_RESULT const void * element
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
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)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
const T * end() const
Definition BLI_array.hh:325
const T * begin() const
Definition BLI_array.hh:321
bool add_overwrite(const Key &key, const Value &value)
Definition BLI_map.hh:325
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool contains(const Key &key) const
Definition BLI_map.hh:353
ItemIterator items() const &
Definition BLI_map.hh:902
int64_t size() const
void append(const T &value)
IndexRange index_range() const
void reserve(const int64_t min_capacity)
nullptr float
#define powf(x, y)
#define roundf(x)
KDTree_3d * tree
uint pos
#define select(A, B, C)
float distance(VecOp< float, D >, VecOp< float, D >) RET
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)
T round(const T &a)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
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)
int RNA_int_get(PointerRNA *ptr, const char *name)
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)
const char * RNA_property_identifier(const PropertyRNA *prop)
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)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#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 BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
int totvertsel
int totloop
CustomData ldata
Definition DNA_ID.h:414
short source
int active_tile_index
struct bNodeTree * nodetree
MeshRuntimeHandle * runtime
CustomData corner_data
struct ToolSettings * toolsettings
float cursor[2]
int tile_grid_shape[2]
int custom_grid_subdiv[2]
struct Image * image
Bounds< float2 > bounds
UvElement * storage
struct ID * id
int16_t type_legacy
void * storage
void operator_context_set(blender::wm::OpCallContext opcontext)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
int mval[2]
Definition WM_types.hh:763
bool(* poll)(struct bContext *)
const char * idname
Definition WM_types.hh:1035
struct ReportList * reports
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 uvedit_face_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMFace *f, bool select)
void UV_OT_copy_mirrored_faces(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_custom_region_set(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 uvedit_edge_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMLoop *l, bool select)
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 uvedit_vert_select_set_no_sync(const ToolSettings *ts, const BMesh *bm, BMLoop *l, bool select)
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)
void ED_uvedit_select_all(const ToolSettings *ts, BMesh *bm)
static wmOperatorStatus uv_seams_from_islands_exec(bContext *C, wmOperator *op)
static wmOperatorStatus uv_mark_seam_invoke(bContext *C, wmOperator *op, const wmEvent *)
UVAlignIslandMode
static void UV_OT_cursor_set(wmOperatorType *ot)
UVAlignIslandOrder
@ LargeToSmall
@ SmallToLarge
@ Fixed
eUVEndPointPrecedence
@ UVEP_PINNED
@ UVEP_SELECTED
@ UVEP_INVALID
static void uv_weld(bContext *C)
void ED_operatortypes_uvedit()
void UV_OT_copy_mirrored_faces(wmOperatorType *ot)
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)
#define UV_VERT_SEL_TEST(ts, bm, l, bool_test)
static void UV_OT_arrange_islands(wmOperatorType *ot)
UVMoveDirection
static wmOperatorStatus uv_remove_doubles_to_selected_shared_vertex(bContext *C, wmOperator *op)
bool ED_uvedit_center_from_pivot_ex(const SpaceImage *sima, Scene *scene, ViewLayer *view_layer, float r_center[2], char mode, bool *r_has_select)
#define UV_EDGE_SEL_TEST(ts, bm, l, bool_test)
static bool uvedit_uv_islands_arrange(const Scene *scene, BMesh *bm, const UVAlignIslandAxis axis, const UVAlignIslandMode align, const UVAlignIslandOrder order, const float margin, float2 &position)
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(const ToolSettings *ts, const BMesh *bm, BMFace *f, bool select_test)
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_align_poll_property(const bContext *, wmOperator *op, const PropertyRNA *prop)
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:73
static wmOperatorStatus uv_weld_exec(bContext *C, wmOperator *)
static wmOperatorStatus uv_hide_exec(bContext *C, wmOperator *op)
UVMoveType
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)
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)
static eUVEndPointPrecedence uvedit_line_update_get_precedence(const bool pinned)
UVAlignPositionMode
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])
static void uv_align(bContext *C, eUVWeldAlign tool, UVAlignPositionMode position_mode)
void ED_uvedit_foreach_uv(const Scene *scene, BMesh *bm, const bool skip_invisible, const bool selected, FunctionRef< void(float[2])> user_fn)
UVAlignInitialPosition
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)
static bool uv_copy_mirrored_faces(const Scene *scene, BMesh *bm, int direction, int precision, int *r_double_warn)
static wmOperatorStatus uv_arrange_islands_exec(bContext *C, wmOperator *op)
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 void UV_OT_move_on_axis(wmOperatorType *ot)
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])
UVAlignIslandAxis
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:92
static wmOperatorStatus uv_set_2d_cursor_exec(bContext *C, wmOperator *op)
static void UV_OT_pin(wmOperatorType *ot)
static wmOperatorStatus uv_copy_mirrored_faces_exec(bContext *C, wmOperator *op)
static bool uv_mesh_hide_sync_select(const ToolSettings *ts, Object *ob, BMEditMesh *em, bool swap)
void ED_operatormacros_uvedit()
static void UV_OT_remove_doubles(wmOperatorType *ot)
static bool is_image_texture_node(bNode *node)
static wmOperatorStatus uv_move_on_axis_exec(bContext *C, wmOperator *op)
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:4237
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:895
#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)