1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup eduv
22  */
23 
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "MEM_guardedalloc.h"
29 
30 #include "DNA_image_types.h"
31 #include "DNA_material_types.h"
32 #include "DNA_mesh_types.h"
33 #include "DNA_meshdata_types.h"
34 #include "DNA_node_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_space_types.h"
38 
39 #include "BLI_array.h"
40 #include "BLI_kdtree.h"
41 #include "BLI_math.h"
42 #include "BLI_utildefines.h"
43 
44 #include "BLT_translation.h"
45 
46 #include "BKE_context.h"
47 #include "BKE_customdata.h"
48 #include "BKE_editmesh.h"
49 #include "BKE_layer.h"
50 #include "BKE_main.h"
51 #include "BKE_material.h"
52 #include "BKE_mesh_mapping.h"
53 #include "BKE_node.h"
54 
55 #include "DEG_depsgraph.h"
56 
57 #include "ED_image.h"
58 #include "ED_mesh.h"
59 #include "ED_node.h"
60 #include "ED_screen.h"
61 #include "ED_uvedit.h"
62 
63 #include "RNA_access.h"
64 #include "RNA_define.h"
65 
66 #include "WM_api.h"
67 #include "WM_message.h"
68 #include "WM_types.h"
69 
70 #include "UI_interface.h"
71 #include "UI_resources.h"
72 #include "UI_view2d.h"
73 
74 #include "uvedit_intern.h"
75 
76 /* -------------------------------------------------------------------- */
77 /** \name State Testing
78  * \{ */
79 
ED_uvedit_test(Object * obedit)80 bool ED_uvedit_test(Object *obedit)
81 {
82   BMEditMesh *em;
83   int ret;
84 
85   if (!obedit) {
86     return 0;
87   }
88 
89   if (obedit->type != OB_MESH) {
90     return 0;
91   }
92 
93   em = BKE_editmesh_from_object(obedit);
94   ret = EDBM_uv_check(em);
95 
96   return ret;
97 }
98 
UNUSED_FUNCTION(ED_operator_uvmap_mesh)99 static int UNUSED_FUNCTION(ED_operator_uvmap_mesh)(bContext *C)
100 {
101   Object *ob = CTX_data_active_object(C);
102 
103   if (ob && ob->type == OB_MESH) {
104     Mesh *me = ob->data;
105 
106     if (CustomData_get_layer(&me->ldata, CD_MLOOPUV) != NULL) {
107       return 1;
108     }
109   }
110 
111   return 0;
112 }
113 
114 /** \} */
115 
116 /* -------------------------------------------------------------------- */
117 /** \name Object Active Image
118  * \{ */
119 
is_image_texture_node(bNode * node)120 static bool is_image_texture_node(bNode *node)
121 {
122   return ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT);
123 }
124 
ED_object_get_active_image(Object * ob,int mat_nr,Image ** r_ima,ImageUser ** r_iuser,bNode ** r_node,bNodeTree ** r_ntree)125 bool ED_object_get_active_image(Object *ob,
126                                 int mat_nr,
127                                 Image **r_ima,
128                                 ImageUser **r_iuser,
129                                 bNode **r_node,
130                                 bNodeTree **r_ntree)
131 {
132   Material *ma = BKE_object_material_get(ob, mat_nr);
133   bNodeTree *ntree = (ma && ma->use_nodes) ? ma->nodetree : NULL;
134   bNode *node = (ntree) ? nodeGetActiveTexture(ntree) : NULL;
135 
136   if (node && is_image_texture_node(node)) {
137     if (r_ima) {
138       *r_ima = (Image *)node->id;
139     }
140     if (r_iuser) {
141       if (node->type == SH_NODE_TEX_IMAGE) {
142         *r_iuser = &((NodeTexImage *)node->storage)->iuser;
143       }
144       else if (node->type == SH_NODE_TEX_ENVIRONMENT) {
145         *r_iuser = &((NodeTexEnvironment *)node->storage)->iuser;
146       }
147       else {
148         *r_iuser = NULL;
149       }
150     }
151     if (r_node) {
152       *r_node = node;
153     }
154     if (r_ntree) {
155       *r_ntree = ntree;
156     }
157     return true;
158   }
159 
160   if (r_ima) {
161     *r_ima = NULL;
162   }
163   if (r_iuser) {
164     *r_iuser = NULL;
165   }
166   if (r_node) {
167     *r_node = node;
168   }
169   if (r_ntree) {
170     *r_ntree = ntree;
171   }
172 
173   return false;
174 }
175 
ED_object_assign_active_image(Main * bmain,Object * ob,int mat_nr,Image * ima)176 void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima)
177 {
178   Material *ma = BKE_object_material_get(ob, mat_nr);
179   bNode *node = (ma && ma->use_nodes) ? nodeGetActiveTexture(ma->nodetree) : NULL;
180 
181   if (node && is_image_texture_node(node)) {
182     node->id = &ima->id;
183     ED_node_tag_update_nodetree(bmain, ma->nodetree, node);
184   }
185 }
186 
187 /** \} */
188 
189 /* -------------------------------------------------------------------- */
190 /** \name Space Conversion
191  * \{ */
192 
uvedit_pixel_to_float(SpaceImage * sima,float pixeldist,float r_dist[2])193 void uvedit_pixel_to_float(SpaceImage *sima, float pixeldist, float r_dist[2])
194 {
195   int width, height;
196 
197   if (sima) {
198     ED_space_image_get_size(sima, &width, &height);
199   }
200   else {
201     width = IMG_SIZE_FALLBACK;
202     height = IMG_SIZE_FALLBACK;
203   }
204 
205   r_dist[0] = pixeldist / width;
206   r_dist[1] = pixeldist / height;
207 }
208 
209 /** \} */
210 
211 /* -------------------------------------------------------------------- */
212 /** \name Live Unwrap Utilities
213  * \{ */
214 
uvedit_live_unwrap_update(SpaceImage * sima,Scene * scene,Object * obedit)215 void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
216 {
217   if (sima && (sima->flag & SI_LIVE_UNWRAP)) {
218     ED_uvedit_live_unwrap_begin(scene, obedit);
219     ED_uvedit_live_unwrap_re_solve();
220     ED_uvedit_live_unwrap_end(0);
221   }
222 }
223 
224 /** \} */
225 
226 /* -------------------------------------------------------------------- */
227 /** \name Geometric Utilities
228  * \{ */
229 
uv_poly_copy_aspect(float uv_orig[][2],float uv[][2],float aspx,float aspy,int len)230 void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len)
231 {
232   int i;
233   for (i = 0; i < len; i++) {
234     uv[i][0] = uv_orig[i][0] * aspx;
235     uv[i][1] = uv_orig[i][1] * aspy;
236   }
237 }
238 
ED_uvedit_minmax_multi(const Scene * scene,Object ** objects_edit,uint objects_len,float r_min[2],float r_max[2])239 bool ED_uvedit_minmax_multi(
240     const Scene *scene, Object **objects_edit, uint objects_len, float r_min[2], float r_max[2])
241 {
242   bool changed = false;
243   INIT_MINMAX2(r_min, r_max);
244 
245   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
246     Object *obedit = objects_edit[ob_index];
247 
248     BMEditMesh *em = BKE_editmesh_from_object(obedit);
249     BMFace *efa;
250     BMLoop *l;
251     BMIter iter, liter;
252     MLoopUV *luv;
253 
254     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
255 
256     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
257       if (!uvedit_face_visible_test(scene, efa)) {
258         continue;
259       }
260 
261       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
262         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
263           luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
264           minmax_v2v2_v2(r_min, r_max, luv->uv);
265           changed = true;
266         }
267       }
268     }
269   }
270   return changed;
271 }
272 
ED_uvedit_minmax(const Scene * scene,Object * obedit,float r_min[2],float r_max[2])273 bool ED_uvedit_minmax(const Scene *scene, Object *obedit, float r_min[2], float r_max[2])
274 {
275   return ED_uvedit_minmax_multi(scene, &obedit, 1, r_min, r_max);
276 }
277 
278 /* Be careful when using this, it bypasses all synchronization options */
ED_uvedit_select_all(BMesh * bm)279 void ED_uvedit_select_all(BMesh *bm)
280 {
281   BMFace *efa;
282   BMLoop *l;
283   BMIter iter, liter;
284   MLoopUV *luv;
285 
286   const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
287 
288   BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
289     BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
290       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
291       luv->flag |= MLOOPUV_VERTSEL;
292     }
293   }
294 }
295 
ED_uvedit_median_multi(const Scene * scene,Object ** objects_edit,uint objects_len,float co[2])296 static bool ED_uvedit_median_multi(const Scene *scene,
297                                    Object **objects_edit,
298                                    uint objects_len,
299                                    float co[2])
300 {
301   uint sel = 0;
302   zero_v2(co);
303 
304   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
305     Object *obedit = objects_edit[ob_index];
306 
307     BMEditMesh *em = BKE_editmesh_from_object(obedit);
308     BMFace *efa;
309     BMLoop *l;
310     BMIter iter, liter;
311     MLoopUV *luv;
312 
313     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
314 
315     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
316       if (!uvedit_face_visible_test(scene, efa)) {
317         continue;
318       }
319 
320       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
321         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
322         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
323           add_v2_v2(co, luv->uv);
324           sel++;
325         }
326       }
327     }
328   }
329 
330   mul_v2_fl(co, 1.0f / (float)sel);
331 
332   return (sel != 0);
333 }
334 
ED_uvedit_center_multi(const Scene * scene,Object ** objects_edit,uint objects_len,float cent[2],char mode)335 bool ED_uvedit_center_multi(
336     const Scene *scene, Object **objects_edit, uint objects_len, float cent[2], char mode)
337 {
338   bool changed = false;
339 
340   if (mode == V3D_AROUND_CENTER_BOUNDS) { /* bounding box */
341     float min[2], max[2];
342     if (ED_uvedit_minmax_multi(scene, objects_edit, objects_len, min, max)) {
343       mid_v2_v2v2(cent, min, max);
344       changed = true;
345     }
346   }
347   else {
348     if (ED_uvedit_median_multi(scene, objects_edit, objects_len, cent)) {
349       changed = true;
350     }
351   }
352 
353   return changed;
354 }
355 
ED_uvedit_center_from_pivot_ex(SpaceImage * sima,Scene * scene,ViewLayer * view_layer,float r_center[2],char mode,bool * r_has_select)356 bool ED_uvedit_center_from_pivot_ex(SpaceImage *sima,
357                                     Scene *scene,
358                                     ViewLayer *view_layer,
359                                     float r_center[2],
360                                     char mode,
361                                     bool *r_has_select)
362 {
363   bool changed = false;
364   switch (mode) {
365     case V3D_AROUND_CURSOR: {
366       copy_v2_v2(r_center, sima->cursor);
367       changed = true;
368       if (r_has_select != NULL) {
369         uint objects_len = 0;
370         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
371             view_layer, ((View3D *)NULL), &objects_len);
372         *r_has_select = uvedit_select_is_any_selected_multi(scene, objects, objects_len);
373         MEM_freeN(objects);
374       }
375       break;
376     }
377     default: {
378       uint objects_len = 0;
379       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
380           view_layer, ((View3D *)NULL), &objects_len);
381       changed = ED_uvedit_center_multi(scene, objects, objects_len, r_center, mode);
382       MEM_freeN(objects);
383       if (r_has_select != NULL) {
384         *r_has_select = changed;
385       }
386       break;
387     }
388   }
389   return changed;
390 }
391 
ED_uvedit_center_from_pivot(SpaceImage * sima,Scene * scene,ViewLayer * view_layer,float r_center[2],char mode)392 bool ED_uvedit_center_from_pivot(
393     SpaceImage *sima, Scene *scene, ViewLayer *view_layer, float r_center[2], char mode)
394 {
395   return ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, mode, NULL);
396 }
397 
398 /** \} */
399 
400 /* -------------------------------------------------------------------- */
401 /** \name Weld Align Operator
402  * \{ */
403 
404 typedef enum eUVWeldAlign {
405   UV_STRAIGHTEN,
406   UV_STRAIGHTEN_X,
407   UV_STRAIGHTEN_Y,
408   UV_ALIGN_AUTO,
409   UV_ALIGN_X,
410   UV_ALIGN_Y,
411   UV_WELD,
412 } eUVWeldAlign;
413 
uv_weld_align(bContext * C,eUVWeldAlign tool)414 static void uv_weld_align(bContext *C, eUVWeldAlign tool)
415 {
416   Scene *scene = CTX_data_scene(C);
417   ViewLayer *view_layer = CTX_data_view_layer(C);
418   SpaceImage *sima = CTX_wm_space_image(C);
419   const ToolSettings *ts = scene->toolsettings;
420   const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
421   float cent[2], min[2], max[2];
422 
423   INIT_MINMAX2(min, max);
424 
425   uint objects_len = 0;
426   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
427       view_layer, ((View3D *)NULL), &objects_len);
428 
429   if (tool == UV_ALIGN_AUTO) {
430     for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
431       Object *obedit = objects[ob_index];
432       BMEditMesh *em = BKE_editmesh_from_object(obedit);
433 
434       if (synced_selection && (em->bm->totvertsel == 0)) {
435         continue;
436       }
437 
438       const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
439 
440       BMIter iter, liter;
441       BMFace *efa;
442       BMLoop *l;
443 
444       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
445         if (!uvedit_face_visible_test(scene, efa)) {
446           continue;
447         }
448 
449         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
450           if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
451             MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
452             minmax_v2v2_v2(min, max, luv->uv);
453           }
454         }
455       }
456     }
457     tool = (max[0] - min[0] >= max[1] - min[1]) ? UV_ALIGN_Y : UV_ALIGN_X;
458   }
459 
460   ED_uvedit_center_multi(scene, objects, objects_len, cent, 0);
461 
462   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
463     Object *obedit = objects[ob_index];
464     BMEditMesh *em = BKE_editmesh_from_object(obedit);
465     bool changed = false;
466 
467     if (synced_selection && (em->bm->totvertsel == 0)) {
468       continue;
469     }
470 
471     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
472 
473     if (ELEM(tool, UV_ALIGN_X, UV_WELD)) {
474       BMIter iter, liter;
475       BMFace *efa;
476       BMLoop *l;
477 
478       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
479         if (!uvedit_face_visible_test(scene, efa)) {
480           continue;
481         }
482 
483         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
484           if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
485             MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
486             luv->uv[0] = cent[0];
487             changed = true;
488           }
489         }
490       }
491     }
492 
493     if (ELEM(tool, UV_ALIGN_Y, UV_WELD)) {
494       BMIter iter, liter;
495       BMFace *efa;
496       BMLoop *l;
497 
498       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
499         if (!uvedit_face_visible_test(scene, efa)) {
500           continue;
501         }
502 
503         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
504           if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
505             MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
506             luv->uv[1] = cent[1];
507             changed = true;
508           }
509         }
510       }
511     }
512 
513     if (ELEM(tool, UV_STRAIGHTEN, UV_STRAIGHTEN_X, UV_STRAIGHTEN_Y)) {
514       BMEdge *eed;
515       BMLoop *l;
516       BMVert *eve;
517       BMVert *eve_start;
518       BMIter iter, liter, eiter;
519 
520       /* clear tag */
521       BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
522 
523       /* tag verts with a selected UV */
524       BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
525         BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
526           if (!uvedit_face_visible_test(scene, l->f)) {
527             continue;
528           }
529 
530           if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
531             BM_elem_flag_enable(eve, BM_ELEM_TAG);
532             break;
533           }
534         }
535       }
536 
537       /* flush vertex tags to edges */
538       BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
539         BM_elem_flag_set(
540             eed,
541             BM_ELEM_TAG,
542             (BM_elem_flag_test(eed->v1, BM_ELEM_TAG) && BM_elem_flag_test(eed->v2, BM_ELEM_TAG)));
543       }
544 
545       /* find a vertex with only one tagged edge */
546       eve_start = NULL;
547       BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
548         int tot_eed_tag = 0;
549         BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
550           if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
551             tot_eed_tag++;
552           }
553         }
554 
555         if (tot_eed_tag == 1) {
556           eve_start = eve;
557           break;
558         }
559       }
560 
561       if (eve_start) {
562         BMVert **eve_line = NULL;
563         BMVert *eve_next = NULL;
564         BLI_array_declare(eve_line);
565         int i;
566 
567         eve = eve_start;
568 
569         /* walk over edges, building an array of verts in a line */
570         while (eve) {
571           BLI_array_append(eve_line, eve);
572           /* don't touch again */
573           BM_elem_flag_disable(eve, BM_ELEM_TAG);
574 
575           eve_next = NULL;
576 
577           /* find next eve */
578           BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
579             if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
580               BMVert *eve_other = BM_edge_other_vert(eed, eve);
581               if (BM_elem_flag_test(eve_other, BM_ELEM_TAG)) {
582                 /* this is a tagged vert we didn't walk over yet, step onto it */
583                 eve_next = eve_other;
584                 break;
585               }
586             }
587           }
588 
589           eve = eve_next;
590         }
591 
592         /* now we have all verts, make into a line */
593         if (BLI_array_len(eve_line) > 2) {
594 
595           /* we know the returns from these must be valid */
596           const float *uv_start = uvedit_first_selected_uv_from_vertex(
597               scene, eve_line[0], cd_loop_uv_offset);
598           const float *uv_end = uvedit_first_selected_uv_from_vertex(
599               scene, eve_line[BLI_array_len(eve_line) - 1], cd_loop_uv_offset);
600           /* For UV_STRAIGHTEN_X & UV_STRAIGHTEN_Y modes */
601           float a = 0.0f;
602           eUVWeldAlign tool_local = tool;
603 
604           if (tool_local == UV_STRAIGHTEN_X) {
605             if (uv_start[1] == uv_end[1]) {
606               tool_local = UV_STRAIGHTEN;
607             }
608             else {
609               a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
610             }
611           }
612           else if (tool_local == UV_STRAIGHTEN_Y) {
613             if (uv_start[0] == uv_end[0]) {
614               tool_local = UV_STRAIGHTEN;
615             }
616             else {
617               a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
618             }
619           }
620 
621           /* go over all verts except for endpoints */
622           for (i = 0; i < BLI_array_len(eve_line); i++) {
623             BM_ITER_ELEM (l, &liter, eve_line[i], BM_LOOPS_OF_VERT) {
624               if (!uvedit_face_visible_test(scene, l->f)) {
625                 continue;
626               }
627 
628               if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
629                 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
630                 /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
631                  * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
632                  * Maybe this should be a BLI func? Or is it already existing?
633                  * Could use interp_v2_v2v2, but not sure it's worth it here...*/
634                 if (tool_local == UV_STRAIGHTEN_X) {
635                   luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0];
636                 }
637                 else if (tool_local == UV_STRAIGHTEN_Y) {
638                   luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1];
639                 }
640                 else {
641                   closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end);
642                 }
643                 changed = true;
644               }
645             }
646           }
647         }
648         else {
649           /* error - not a line, needs 3+ points  */
650         }
651 
652         if (eve_line) {
653           MEM_freeN(eve_line);
654         }
655       }
656       else {
657         /* error - cant find an endpoint */
658       }
659     }
660 
661     if (changed) {
662       uvedit_live_unwrap_update(sima, scene, obedit);
663       DEG_id_tag_update(obedit->data, 0);
664       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
665     }
666   }
667 
668   MEM_freeN(objects);
669 }
670 
uv_align_exec(bContext * C,wmOperator * op)671 static int uv_align_exec(bContext *C, wmOperator *op)
672 {
673   uv_weld_align(C, RNA_enum_get(op->ptr, "axis"));
674 
675   return OPERATOR_FINISHED;
676 }
677 
UV_OT_align(wmOperatorType * ot)678 static void UV_OT_align(wmOperatorType *ot)
679 {
680   static const EnumPropertyItem axis_items[] = {
681       {UV_STRAIGHTEN,
682        "ALIGN_S",
683        0,
684        "Straighten",
685        "Align UVs along the line defined by the endpoints"},
686       {UV_STRAIGHTEN_X,
687        "ALIGN_T",
688        0,
689        "Straighten X",
690        "Align UVs along the line defined by the endpoints along the X axis"},
691       {UV_STRAIGHTEN_Y,
692        "ALIGN_U",
693        0,
694        "Straighten Y",
695        "Align UVs along the line defined by the endpoints along the Y axis"},
696       {UV_ALIGN_AUTO,
697        "ALIGN_AUTO",
698        0,
699        "Align Auto",
700        "Automatically choose the axis on which there is most alignment already"},
701       {UV_ALIGN_X, "ALIGN_X", 0, "Align X", "Align UVs on X axis"},
702       {UV_ALIGN_Y, "ALIGN_Y", 0, "Align Y", "Align UVs on Y axis"},
703       {0, NULL, 0, NULL, NULL},
704   };
705 
706   /* identifiers */
707   ot->name = "Align";
708   ot->description = "Align selected UV vertices to an axis";
709   ot->idname = "UV_OT_align";
710   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
711 
712   /* api callbacks */
713   ot->exec = uv_align_exec;
714   ot->poll = ED_operator_uvedit;
715 
716   /* properties */
717   RNA_def_enum(
718       ot->srna, "axis", axis_items, UV_ALIGN_AUTO, "Axis", "Axis to align UV locations on");
719 }
720 
721 /** \} */
722 
723 /* -------------------------------------------------------------------- */
724 /** \name Remove Doubles Operator
725  * \{ */
726 
uv_remove_doubles_to_selected(bContext * C,wmOperator * op)727 static int uv_remove_doubles_to_selected(bContext *C, wmOperator *op)
728 {
729   Scene *scene = CTX_data_scene(C);
730   ViewLayer *view_layer = CTX_data_view_layer(C);
731   SpaceImage *sima = CTX_wm_space_image(C);
732   const ToolSettings *ts = scene->toolsettings;
733 
734   const float threshold = RNA_float_get(op->ptr, "threshold");
735   const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
736 
737   uint objects_len = 0;
738   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
739       view_layer, ((View3D *)NULL), &objects_len);
740 
741   bool *changed = MEM_callocN(sizeof(bool) * objects_len, "uv_remove_doubles_selected.changed");
742 
743   /* Maximum index of an objects[i]'s MLoopUVs in MLoopUV_arr.
744    * It helps find which MLoopUV in *MLoopUV_arr belongs to which object. */
745   uint *ob_mloopuv_max_idx = MEM_callocN(sizeof(uint) * objects_len,
746                                          "uv_remove_doubles_selected.ob_mloopuv_max_idx");
747 
748   /* Calculate max possible number of kdtree nodes. */
749   int uv_maxlen = 0;
750   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
751     Object *obedit = objects[ob_index];
752     BMEditMesh *em = BKE_editmesh_from_object(obedit);
753 
754     if (synced_selection && (em->bm->totvertsel == 0)) {
755       continue;
756     }
757 
758     uv_maxlen += em->bm->totloop;
759   }
760 
761   KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
762 
763   int *duplicates = NULL;
764   BLI_array_declare(duplicates);
765 
766   MLoopUV **mloopuv_arr = NULL;
767   BLI_array_declare(mloopuv_arr);
768 
769   int mloopuv_count = 0; /* Also used for *duplicates count. */
770 
771   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
772     BMIter iter, liter;
773     BMFace *efa;
774     BMLoop *l;
775     Object *obedit = objects[ob_index];
776     BMEditMesh *em = BKE_editmesh_from_object(obedit);
777 
778     if (synced_selection && (em->bm->totvertsel == 0)) {
779       continue;
780     }
781 
782     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
783 
784     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
785       if (!uvedit_face_visible_test(scene, efa)) {
786         continue;
787       }
788 
789       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
790         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
791           MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
792           BLI_kdtree_2d_insert(tree, mloopuv_count, luv->uv);
793           BLI_array_append(duplicates, -1);
794           BLI_array_append(mloopuv_arr, luv);
795           mloopuv_count++;
796         }
797       }
798     }
799 
800     ob_mloopuv_max_idx[ob_index] = mloopuv_count - 1;
801   }
802 
803   BLI_kdtree_2d_balance(tree);
804   int found_duplicates = BLI_kdtree_2d_calc_duplicates_fast(tree, threshold, false, duplicates);
805 
806   if (found_duplicates > 0) {
807     /* Calculate average uv for duplicates. */
808     int *uv_duplicate_count = MEM_callocN(sizeof(int) * mloopuv_count,
809                                           "uv_remove_doubles_selected.uv_duplicate_count");
810     for (int i = 0; i < mloopuv_count; i++) {
811       if (duplicates[i] == -1) { /* If doesn't reference another */
812         uv_duplicate_count[i]++; /* self */
813         continue;
814       }
815 
816       if (duplicates[i] != i) {
817         /* If not self then accumulate uv for averaging.
818          * Self uv is already present in accumulator */
819         add_v2_v2(mloopuv_arr[duplicates[i]]->uv, mloopuv_arr[i]->uv);
820       }
821       uv_duplicate_count[duplicates[i]]++;
822     }
823 
824     for (int i = 0; i < mloopuv_count; i++) {
825       if (uv_duplicate_count[i] < 2) {
826         continue;
827       }
828 
829       mul_v2_fl(mloopuv_arr[i]->uv, 1.0f / (float)uv_duplicate_count[i]);
830     }
831     MEM_freeN(uv_duplicate_count);
832 
833     /* Update duplicated uvs. */
834     uint ob_index = 0;
835     for (int i = 0; i < mloopuv_count; i++) {
836       /* Make sure we know which object owns the MLoopUV at this index.
837        * Remember that in some cases the object will have no loop uv,
838        * thus we need the while loop, and not simply an if check. */
839       while (ob_mloopuv_max_idx[ob_index] < i) {
840         ob_index++;
841       }
842 
843       if (duplicates[i] == -1) {
844         continue;
845       }
846 
847       copy_v2_v2(mloopuv_arr[i]->uv, mloopuv_arr[duplicates[i]]->uv);
848       changed[ob_index] = true;
849     }
850 
851     for (ob_index = 0; ob_index < objects_len; ob_index++) {
852       if (changed[ob_index]) {
853         Object *obedit = objects[ob_index];
854         uvedit_live_unwrap_update(sima, scene, obedit);
855         DEG_id_tag_update(obedit->data, 0);
856         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
857       }
858     }
859   }
860 
861   BLI_kdtree_2d_free(tree);
862   BLI_array_free(mloopuv_arr);
863   BLI_array_free(duplicates);
864   MEM_freeN(changed);
865   MEM_freeN(objects);
866   MEM_freeN(ob_mloopuv_max_idx);
867 
868   return OPERATOR_FINISHED;
869 }
870 
uv_remove_doubles_to_unselected(bContext * C,wmOperator * op)871 static int uv_remove_doubles_to_unselected(bContext *C, wmOperator *op)
872 {
873   Scene *scene = CTX_data_scene(C);
874   ViewLayer *view_layer = CTX_data_view_layer(C);
875   SpaceImage *sima = CTX_wm_space_image(C);
876   const ToolSettings *ts = scene->toolsettings;
877 
878   const float threshold = RNA_float_get(op->ptr, "threshold");
879   const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
880 
881   uint objects_len = 0;
882   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
883       view_layer, ((View3D *)NULL), &objects_len);
884 
885   /* Calculate max possible number of kdtree nodes. */
886   int uv_maxlen = 0;
887   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
888     Object *obedit = objects[ob_index];
889     BMEditMesh *em = BKE_editmesh_from_object(obedit);
890     uv_maxlen += em->bm->totloop;
891   }
892 
893   KDTree_2d *tree = BLI_kdtree_2d_new(uv_maxlen);
894 
895   MLoopUV **mloopuv_arr = NULL;
896   BLI_array_declare(mloopuv_arr);
897 
898   int mloopuv_count = 0;
899 
900   /* Add visible non-selected uvs to tree */
901   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
902     BMIter iter, liter;
903     BMFace *efa;
904     BMLoop *l;
905     Object *obedit = objects[ob_index];
906     BMEditMesh *em = BKE_editmesh_from_object(obedit);
907 
908     if (synced_selection && (em->bm->totvertsel == em->bm->totvert)) {
909       continue;
910     }
911 
912     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
913 
914     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
915       if (!uvedit_face_visible_test(scene, efa)) {
916         continue;
917       }
918 
919       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
920         if (!uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
921           MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
922           BLI_kdtree_2d_insert(tree, mloopuv_count, luv->uv);
923           BLI_array_append(mloopuv_arr, luv);
924           mloopuv_count++;
925         }
926       }
927     }
928   }
929 
930   BLI_kdtree_2d_balance(tree);
931 
932   /* For each selected uv, find duplicate non selected uv. */
933   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
934     BMIter iter, liter;
935     BMFace *efa;
936     BMLoop *l;
937     bool changed = false;
938     Object *obedit = objects[ob_index];
939     BMEditMesh *em = BKE_editmesh_from_object(obedit);
940 
941     if (synced_selection && (em->bm->totvertsel == 0)) {
942       continue;
943     }
944 
945     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
946 
947     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
948       if (!uvedit_face_visible_test(scene, efa)) {
949         continue;
950       }
951 
952       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
953         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
954           MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
955           KDTreeNearest_2d nearest;
956           const int i = BLI_kdtree_2d_find_nearest(tree, luv->uv, &nearest);
957 
958           if (i != -1 && nearest.dist < threshold) {
959             copy_v2_v2(luv->uv, mloopuv_arr[i]->uv);
960             changed = true;
961           }
962         }
963       }
964     }
965 
966     if (changed) {
967       uvedit_live_unwrap_update(sima, scene, obedit);
968       DEG_id_tag_update(obedit->data, 0);
969       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
970     }
971   }
972 
973   BLI_kdtree_2d_free(tree);
974   BLI_array_free(mloopuv_arr);
975   MEM_freeN(objects);
976 
977   return OPERATOR_FINISHED;
978 }
979 
uv_remove_doubles_exec(bContext * C,wmOperator * op)980 static int uv_remove_doubles_exec(bContext *C, wmOperator *op)
981 {
982   if (RNA_boolean_get(op->ptr, "use_unselected")) {
983     return uv_remove_doubles_to_unselected(C, op);
984   }
985   return uv_remove_doubles_to_selected(C, op);
986 }
987 
UV_OT_remove_doubles(wmOperatorType * ot)988 static void UV_OT_remove_doubles(wmOperatorType *ot)
989 {
990   /* identifiers */
991   ot->name = "Merge UVs by Distance";
992   ot->description =
993       "Selected UV vertices that are within a radius of each other are welded together";
994   ot->idname = "UV_OT_remove_doubles";
995   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
996 
997   /* api callbacks */
998   ot->exec = uv_remove_doubles_exec;
999   ot->poll = ED_operator_uvedit;
1000 
1001   RNA_def_float(ot->srna,
1002                 "threshold",
1003                 0.02f,
1004                 0.0f,
1005                 10.0f,
1006                 "Merge Distance",
1007                 "Maximum distance between welded vertices",
1008                 0.0f,
1009                 1.0f);
1010   RNA_def_boolean(
1011       ot->srna, "use_unselected", 0, "Unselected", "Merge selected to other unselected vertices");
1012 }
1013 
1014 /** \} */
1015 
1016 /* -------------------------------------------------------------------- */
1017 /** \name Weld Near Operator
1018  * \{ */
1019 
uv_weld_exec(bContext * C,wmOperator * UNUSED (op))1020 static int uv_weld_exec(bContext *C, wmOperator *UNUSED(op))
1021 {
1022   uv_weld_align(C, UV_WELD);
1023 
1024   return OPERATOR_FINISHED;
1025 }
1026 
UV_OT_weld(wmOperatorType * ot)1027 static void UV_OT_weld(wmOperatorType *ot)
1028 {
1029   /* identifiers */
1030   ot->name = "Weld";
1031   ot->description = "Weld selected UV vertices together";
1032   ot->idname = "UV_OT_weld";
1033   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1034 
1035   /* api callbacks */
1036   ot->exec = uv_weld_exec;
1037   ot->poll = ED_operator_uvedit;
1038 }
1039 
1040 /** \} */
1041 
1042 /* -------------------------------------------------------------------- */
1043 /** \name Snap Cursor Operator
1044  * \{ */
1045 
uv_snap_to_pixel(float uvco[2],float w,float h)1046 static void uv_snap_to_pixel(float uvco[2], float w, float h)
1047 {
1048   uvco[0] = roundf(uvco[0] * w) / w;
1049   uvco[1] = roundf(uvco[1] * h) / h;
1050 }
1051 
uv_snap_cursor_to_pixels(SpaceImage * sima)1052 static void uv_snap_cursor_to_pixels(SpaceImage *sima)
1053 {
1054   int width = 0, height = 0;
1055 
1056   ED_space_image_get_size(sima, &width, &height);
1057   uv_snap_to_pixel(sima->cursor, width, height);
1058 }
1059 
uv_snap_cursor_to_selection(Scene * scene,Object ** objects_edit,uint objects_len,SpaceImage * sima)1060 static bool uv_snap_cursor_to_selection(Scene *scene,
1061                                         Object **objects_edit,
1062                                         uint objects_len,
1063                                         SpaceImage *sima)
1064 {
1065   return ED_uvedit_center_multi(scene, objects_edit, objects_len, sima->cursor, sima->around);
1066 }
1067 
uv_snap_cursor_exec(bContext * C,wmOperator * op)1068 static int uv_snap_cursor_exec(bContext *C, wmOperator *op)
1069 {
1070   SpaceImage *sima = CTX_wm_space_image(C);
1071 
1072   bool changed = false;
1073 
1074   switch (RNA_enum_get(op->ptr, "target")) {
1075     case 0:
1076       uv_snap_cursor_to_pixels(sima);
1077       changed = true;
1078       break;
1079     case 1: {
1080       Scene *scene = CTX_data_scene(C);
1081       ViewLayer *view_layer = CTX_data_view_layer(C);
1082 
1083       uint objects_len = 0;
1084       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1085           view_layer, ((View3D *)NULL), &objects_len);
1086       changed = uv_snap_cursor_to_selection(scene, objects, objects_len, sima);
1087       MEM_freeN(objects);
1088       break;
1089     }
1090   }
1091 
1092   if (!changed) {
1093     return OPERATOR_CANCELLED;
1094   }
1095 
1096   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, sima);
1097 
1098   return OPERATOR_FINISHED;
1099 }
1100 
UV_OT_snap_cursor(wmOperatorType * ot)1101 static void UV_OT_snap_cursor(wmOperatorType *ot)
1102 {
1103   static const EnumPropertyItem target_items[] = {
1104       {0, "PIXELS", 0, "Pixels", ""},
1105       {1, "SELECTED", 0, "Selected", ""},
1106       {0, NULL, 0, NULL, NULL},
1107   };
1108 
1109   /* identifiers */
1110   ot->name = "Snap Cursor";
1111   ot->description = "Snap cursor to target type";
1112   ot->idname = "UV_OT_snap_cursor";
1113   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1114 
1115   /* api callbacks */
1116   ot->exec = uv_snap_cursor_exec;
1117   ot->poll = ED_operator_uvedit_space_image; /* requires space image */
1118 
1119   /* properties */
1120   RNA_def_enum(
1121       ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1122 }
1123 
1124 /** \} */
1125 
1126 /* -------------------------------------------------------------------- */
1127 /** \name Snap Selection Operator
1128  * \{ */
1129 
uv_snap_uvs_to_cursor(Scene * scene,Object * obedit,const float cursor[2])1130 static bool uv_snap_uvs_to_cursor(Scene *scene, Object *obedit, const float cursor[2])
1131 {
1132   BMEditMesh *em = BKE_editmesh_from_object(obedit);
1133   BMFace *efa;
1134   BMLoop *l;
1135   BMIter iter, liter;
1136   MLoopUV *luv;
1137   bool changed = false;
1138 
1139   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1140 
1141   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1142     if (!uvedit_face_visible_test(scene, efa)) {
1143       continue;
1144     }
1145 
1146     BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1147       if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1148         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1149         copy_v2_v2(luv->uv, cursor);
1150         changed = true;
1151       }
1152     }
1153   }
1154 
1155   return changed;
1156 }
1157 
uv_snap_uvs_offset(Scene * scene,Object * obedit,const float offset[2])1158 static bool uv_snap_uvs_offset(Scene *scene, Object *obedit, const float offset[2])
1159 {
1160   BMEditMesh *em = BKE_editmesh_from_object(obedit);
1161   BMFace *efa;
1162   BMLoop *l;
1163   BMIter iter, liter;
1164   MLoopUV *luv;
1165   bool changed = false;
1166 
1167   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1168 
1169   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1170     if (!uvedit_face_visible_test(scene, efa)) {
1171       continue;
1172     }
1173 
1174     BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1175       if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1176         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1177         add_v2_v2(luv->uv, offset);
1178         changed = true;
1179       }
1180     }
1181   }
1182 
1183   return changed;
1184 }
1185 
uv_snap_uvs_to_adjacent_unselected(Scene * scene,Object * obedit)1186 static bool uv_snap_uvs_to_adjacent_unselected(Scene *scene, Object *obedit)
1187 {
1188   BMEditMesh *em = BKE_editmesh_from_object(obedit);
1189   BMesh *bm = em->bm;
1190   BMFace *f;
1191   BMLoop *l, *lsub;
1192   BMIter iter, liter, lsubiter;
1193   MLoopUV *luv;
1194   bool changed = false;
1195   const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
1196 
1197   /* index every vert that has a selected UV using it, but only once so as to
1198    * get unique indices and to count how much to malloc */
1199   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1200     if (uvedit_face_visible_test(scene, f)) {
1201       BM_elem_flag_enable(f, BM_ELEM_TAG);
1202       BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1203         BM_elem_flag_set(l, BM_ELEM_TAG, uvedit_uv_select_test(scene, l, cd_loop_uv_offset));
1204       }
1205     }
1206     else {
1207       BM_elem_flag_disable(f, BM_ELEM_TAG);
1208     }
1209   }
1210 
1211   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1212     if (BM_elem_flag_test(f, BM_ELEM_TAG)) { /* face: visible */
1213       BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
1214         if (BM_elem_flag_test(l, BM_ELEM_TAG)) { /* loop: selected*/
1215           float uv[2] = {0.0f, 0.0f};
1216           int uv_tot = 0;
1217 
1218           BM_ITER_ELEM (lsub, &lsubiter, l->v, BM_LOOPS_OF_VERT) {
1219             if (BM_elem_flag_test(lsub->f, BM_ELEM_TAG) && /* face: visible */
1220                 !BM_elem_flag_test(lsub, BM_ELEM_TAG))     /* loop: unselected  */
1221             {
1222               luv = BM_ELEM_CD_GET_VOID_P(lsub, cd_loop_uv_offset);
1223               add_v2_v2(uv, luv->uv);
1224               uv_tot++;
1225             }
1226           }
1227 
1228           if (uv_tot) {
1229             luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1230             mul_v2_v2fl(luv->uv, uv, 1.0f / (float)uv_tot);
1231             changed = true;
1232           }
1233         }
1234       }
1235     }
1236   }
1237 
1238   return changed;
1239 }
1240 
uv_snap_uvs_to_pixels(SpaceImage * sima,Scene * scene,Object * obedit)1241 static bool uv_snap_uvs_to_pixels(SpaceImage *sima, Scene *scene, Object *obedit)
1242 {
1243   BMEditMesh *em = BKE_editmesh_from_object(obedit);
1244   BMFace *efa;
1245   BMLoop *l;
1246   BMIter iter, liter;
1247   MLoopUV *luv;
1248   int width = 0, height = 0;
1249   float w, h;
1250   bool changed = false;
1251 
1252   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1253 
1254   ED_space_image_get_size(sima, &width, &height);
1255   w = (float)width;
1256   h = (float)height;
1257 
1258   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1259     if (!uvedit_face_visible_test(scene, efa)) {
1260       continue;
1261     }
1262 
1263     BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1264       if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1265         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1266         uv_snap_to_pixel(luv->uv, w, h);
1267       }
1268     }
1269 
1270     changed = true;
1271   }
1272 
1273   return changed;
1274 }
1275 
uv_snap_selection_exec(bContext * C,wmOperator * op)1276 static int uv_snap_selection_exec(bContext *C, wmOperator *op)
1277 {
1278   Scene *scene = CTX_data_scene(C);
1279   ViewLayer *view_layer = CTX_data_view_layer(C);
1280   SpaceImage *sima = CTX_wm_space_image(C);
1281   const ToolSettings *ts = scene->toolsettings;
1282   const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1283   const int target = RNA_enum_get(op->ptr, "target");
1284   float offset[2] = {0};
1285 
1286   uint objects_len = 0;
1287   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1288       view_layer, ((View3D *)NULL), &objects_len);
1289 
1290   if (target == 2) {
1291     float center[2];
1292     if (!ED_uvedit_center_multi(scene, objects, objects_len, center, sima->around)) {
1293       MEM_freeN(objects);
1294       return OPERATOR_CANCELLED;
1295     }
1296     sub_v2_v2v2(offset, sima->cursor, center);
1297   }
1298 
1299   bool changed_multi = false;
1300   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1301     Object *obedit = objects[ob_index];
1302     BMEditMesh *em = BKE_editmesh_from_object(obedit);
1303 
1304     if (synced_selection && (em->bm->totvertsel == 0)) {
1305       continue;
1306     }
1307 
1308     bool changed = false;
1309     switch (target) {
1310       case 0:
1311         changed = uv_snap_uvs_to_pixels(sima, scene, obedit);
1312         break;
1313       case 1:
1314         changed = uv_snap_uvs_to_cursor(scene, obedit, sima->cursor);
1315         break;
1316       case 2:
1317         changed = uv_snap_uvs_offset(scene, obedit, offset);
1318         break;
1319       case 3:
1320         changed = uv_snap_uvs_to_adjacent_unselected(scene, obedit);
1321         break;
1322     }
1323 
1324     if (changed) {
1325       changed_multi = true;
1326       uvedit_live_unwrap_update(sima, scene, obedit);
1327       DEG_id_tag_update(obedit->data, 0);
1328       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1329     }
1330   }
1331   MEM_freeN(objects);
1332 
1333   return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1334 }
1335 
UV_OT_snap_selected(wmOperatorType * ot)1336 static void UV_OT_snap_selected(wmOperatorType *ot)
1337 {
1338   static const EnumPropertyItem target_items[] = {
1339       {0, "PIXELS", 0, "Pixels", ""},
1340       {1, "CURSOR", 0, "Cursor", ""},
1341       {2, "CURSOR_OFFSET", 0, "Cursor (Offset)", ""},
1342       {3, "ADJACENT_UNSELECTED", 0, "Adjacent Unselected", ""},
1343       {0, NULL, 0, NULL, NULL},
1344   };
1345 
1346   /* identifiers */
1347   ot->name = "Snap Selection";
1348   ot->description = "Snap selected UV vertices to target type";
1349   ot->idname = "UV_OT_snap_selected";
1350   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1351 
1352   /* api callbacks */
1353   ot->exec = uv_snap_selection_exec;
1354   ot->poll = ED_operator_uvedit_space_image;
1355 
1356   /* properties */
1357   RNA_def_enum(
1358       ot->srna, "target", target_items, 0, "Target", "Target to snap the selected UVs to");
1359 }
1360 
1361 /** \} */
1362 
1363 /* -------------------------------------------------------------------- */
1364 /** \name Pin UV's Operator
1365  * \{ */
1366 
uv_pin_exec(bContext * C,wmOperator * op)1367 static int uv_pin_exec(bContext *C, wmOperator *op)
1368 {
1369   Scene *scene = CTX_data_scene(C);
1370   ViewLayer *view_layer = CTX_data_view_layer(C);
1371   BMFace *efa;
1372   BMLoop *l;
1373   BMIter iter, liter;
1374   MLoopUV *luv;
1375   const ToolSettings *ts = scene->toolsettings;
1376   const bool clear = RNA_boolean_get(op->ptr, "clear");
1377   const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1378 
1379   uint objects_len = 0;
1380   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1381       view_layer, ((View3D *)NULL), &objects_len);
1382 
1383   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1384     Object *obedit = objects[ob_index];
1385     BMEditMesh *em = BKE_editmesh_from_object(obedit);
1386 
1387     bool changed = false;
1388     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1389 
1390     if (synced_selection && (em->bm->totvertsel == 0)) {
1391       continue;
1392     }
1393 
1394     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1395       if (!uvedit_face_visible_test(scene, efa)) {
1396         continue;
1397       }
1398 
1399       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1400         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1401 
1402         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1403           changed = true;
1404           if (clear) {
1405             luv->flag &= ~MLOOPUV_PINNED;
1406           }
1407           else {
1408             luv->flag |= MLOOPUV_PINNED;
1409           }
1410         }
1411       }
1412     }
1413 
1414     if (changed) {
1415       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1416       DEG_id_tag_update(obedit->data, ID_RECALC_COPY_ON_WRITE);
1417     }
1418   }
1419   MEM_freeN(objects);
1420 
1421   return OPERATOR_FINISHED;
1422 }
1423 
UV_OT_pin(wmOperatorType * ot)1424 static void UV_OT_pin(wmOperatorType *ot)
1425 {
1426   /* identifiers */
1427   ot->name = "Pin";
1428   ot->description =
1429       "Set/clear selected UV vertices as anchored between multiple unwrap operations";
1430   ot->idname = "UV_OT_pin";
1431   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1432 
1433   /* api callbacks */
1434   ot->exec = uv_pin_exec;
1435   ot->poll = ED_operator_uvedit;
1436 
1437   /* properties */
1438   RNA_def_boolean(
1439       ot->srna, "clear", 0, "Clear", "Clear pinning for the selection instead of setting it");
1440 }
1441 
1442 /** \} */
1443 
1444 /* -------------------------------------------------------------------- */
1445 /** \name Hide Operator
1446  * \{ */
1447 
1448 /* check if we are selected or unselected based on 'bool_test' arg,
1449  * needed for select swap support */
1450 #define UV_SEL_TEST(luv, bool_test) \
1451   ((((luv)->flag & MLOOPUV_VERTSEL) == MLOOPUV_VERTSEL) == bool_test)
1452 
1453 /* is every UV vert selected or unselected depending on bool_test */
bm_face_is_all_uv_sel(BMFace * f,bool select_test,const int cd_loop_uv_offset)1454 static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, const int cd_loop_uv_offset)
1455 {
1456   BMLoop *l_iter;
1457   BMLoop *l_first;
1458 
1459   l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1460   do {
1461     MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
1462     if (!UV_SEL_TEST(luv, select_test)) {
1463       return false;
1464     }
1465   } while ((l_iter = l_iter->next) != l_first);
1466 
1467   return true;
1468 }
1469 
uv_hide_exec(bContext * C,wmOperator * op)1470 static int uv_hide_exec(bContext *C, wmOperator *op)
1471 {
1472   ViewLayer *view_layer = CTX_data_view_layer(C);
1473   Scene *scene = CTX_data_scene(C);
1474   const ToolSettings *ts = scene->toolsettings;
1475   const bool swap = RNA_boolean_get(op->ptr, "unselected");
1476   const int use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1477 
1478   uint objects_len = 0;
1479   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1480       view_layer, ((View3D *)NULL), &objects_len);
1481 
1482   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1483     Object *ob = objects[ob_index];
1484     BMEditMesh *em = BKE_editmesh_from_object(ob);
1485     BMFace *efa;
1486     BMLoop *l;
1487     BMIter iter, liter;
1488     MLoopUV *luv;
1489 
1490     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1491 
1492     if (ts->uv_flag & UV_SYNC_SELECTION) {
1493       if (EDBM_mesh_hide(em, swap)) {
1494         EDBM_update_generic(ob->data, true, false);
1495       }
1496       return OPERATOR_FINISHED;
1497     }
1498 
1499     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1500       int hide = 0;
1501 
1502       if (!uvedit_face_visible_test(scene, efa)) {
1503         continue;
1504       }
1505 
1506       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1507         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1508 
1509         if (UV_SEL_TEST(luv, !swap)) {
1510           hide = 1;
1511           break;
1512         }
1513       }
1514 
1515       if (hide) {
1516         /* note, a special case for edges could be used,
1517          * for now edges act like verts and get flushed */
1518         if (use_face_center) {
1519           if (em->selectmode == SCE_SELECT_FACE) {
1520             /* check that every UV is selected */
1521             if (bm_face_is_all_uv_sel(efa, true, cd_loop_uv_offset) == !swap) {
1522               BM_face_select_set(em->bm, efa, false);
1523             }
1524             uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
1525           }
1526           else {
1527             if (bm_face_is_all_uv_sel(efa, true, cd_loop_uv_offset) == !swap) {
1528               BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1529                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1530                 if (UV_SEL_TEST(luv, !swap)) {
1531                   BM_vert_select_set(em->bm, l->v, false);
1532                 }
1533               }
1534             }
1535             if (!swap) {
1536               uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
1537             }
1538           }
1539         }
1540         else if (em->selectmode == SCE_SELECT_FACE) {
1541           /* check if a UV is de-selected */
1542           if (bm_face_is_all_uv_sel(efa, false, cd_loop_uv_offset) != !swap) {
1543             BM_face_select_set(em->bm, efa, false);
1544             uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
1545           }
1546         }
1547         else {
1548           BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1549             luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1550             if (UV_SEL_TEST(luv, !swap)) {
1551               BM_vert_select_set(em->bm, l->v, false);
1552               if (!swap) {
1553                 luv->flag &= ~MLOOPUV_VERTSEL;
1554               }
1555             }
1556           }
1557         }
1558       }
1559     }
1560 
1561     /* flush vertex selection changes */
1562     if (em->selectmode != SCE_SELECT_FACE) {
1563       EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX | SCE_SELECT_EDGE);
1564     }
1565 
1566     BM_select_history_validate(em->bm);
1567 
1568     DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
1569     WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
1570   }
1571 
1572   MEM_freeN(objects);
1573 
1574   return OPERATOR_FINISHED;
1575 }
1576 
1577 #undef UV_SEL_TEST
1578 
UV_OT_hide(wmOperatorType * ot)1579 static void UV_OT_hide(wmOperatorType *ot)
1580 {
1581   /* identifiers */
1582   ot->name = "Hide Selected";
1583   ot->description = "Hide (un)selected UV vertices";
1584   ot->idname = "UV_OT_hide";
1585   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1586 
1587   /* api callbacks */
1588   ot->exec = uv_hide_exec;
1589   ot->poll = ED_operator_uvedit;
1590 
1591   /* props */
1592   RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
1593 }
1594 
1595 /** \} */
1596 
1597 /* -------------------------------------------------------------------- */
1598 /** \name Reveal Operator
1599  * \{ */
1600 
uv_reveal_exec(bContext * C,wmOperator * op)1601 static int uv_reveal_exec(bContext *C, wmOperator *op)
1602 {
1603   ViewLayer *view_layer = CTX_data_view_layer(C);
1604   SpaceImage *sima = CTX_wm_space_image(C);
1605   Scene *scene = CTX_data_scene(C);
1606   const ToolSettings *ts = scene->toolsettings;
1607 
1608   const int use_face_center = (ts->uv_selectmode == UV_SELECT_FACE);
1609   const int stickymode = sima ? (sima->sticky != SI_STICKY_DISABLE) : 1;
1610   const bool select = RNA_boolean_get(op->ptr, "select");
1611 
1612   uint objects_len = 0;
1613   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1614       view_layer, ((View3D *)NULL), &objects_len);
1615 
1616   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1617     Object *ob = objects[ob_index];
1618     BMEditMesh *em = BKE_editmesh_from_object(ob);
1619     BMFace *efa;
1620     BMLoop *l;
1621     BMIter iter, liter;
1622     MLoopUV *luv;
1623 
1624     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1625 
1626     /* note on tagging, selecting faces needs to be delayed so it doesn't select the verts and
1627      * confuse our checks on selected verts. */
1628 
1629     /* call the mesh function if we are in mesh sync sel */
1630     if (ts->uv_flag & UV_SYNC_SELECTION) {
1631       if (EDBM_mesh_reveal(em, select)) {
1632         EDBM_update_generic(ob->data, true, false);
1633       }
1634       return OPERATOR_FINISHED;
1635     }
1636     if (use_face_center) {
1637       if (em->selectmode == SCE_SELECT_FACE) {
1638         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1639           BM_elem_flag_disable(efa, BM_ELEM_TAG);
1640           if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1641             BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1642               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1643               SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1644             }
1645             /* BM_face_select_set(em->bm, efa, true); */
1646             BM_elem_flag_enable(efa, BM_ELEM_TAG);
1647           }
1648         }
1649       }
1650       else {
1651         /* enable adjacent faces to have disconnected UV selections if sticky is disabled */
1652         if (!stickymode) {
1653           BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1654             BM_elem_flag_disable(efa, BM_ELEM_TAG);
1655             if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) &&
1656                 !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1657               int totsel = 0;
1658               BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1659                 totsel += BM_elem_flag_test(l->v, BM_ELEM_SELECT);
1660               }
1661 
1662               if (!totsel) {
1663                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1664                   luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1665                   SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1666                 }
1667                 /* BM_face_select_set(em->bm, efa, true); */
1668                 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1669               }
1670             }
1671           }
1672         }
1673         else {
1674           BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1675             BM_elem_flag_disable(efa, BM_ELEM_TAG);
1676             if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) &&
1677                 !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1678               BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1679                 if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) == 0) {
1680                   luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1681                   SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1682                 }
1683               }
1684               /* BM_face_select_set(em->bm, efa, true); */
1685               BM_elem_flag_enable(efa, BM_ELEM_TAG);
1686             }
1687           }
1688         }
1689       }
1690     }
1691     else if (em->selectmode == SCE_SELECT_FACE) {
1692       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1693         BM_elem_flag_disable(efa, BM_ELEM_TAG);
1694         if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1695           BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1696             luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1697             SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1698           }
1699           /* BM_face_select_set(em->bm, efa, true); */
1700           BM_elem_flag_enable(efa, BM_ELEM_TAG);
1701         }
1702       }
1703     }
1704     else {
1705       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1706         BM_elem_flag_disable(efa, BM_ELEM_TAG);
1707         if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1708           BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1709             if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) == 0) {
1710               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1711               SET_FLAG_FROM_TEST(luv->flag, select, MLOOPUV_VERTSEL);
1712             }
1713           }
1714           /* BM_face_select_set(em->bm, efa, true); */
1715           BM_elem_flag_enable(efa, BM_ELEM_TAG);
1716         }
1717       }
1718     }
1719 
1720     /* re-select tagged faces */
1721     BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG);
1722 
1723     DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
1724     WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
1725   }
1726 
1727   MEM_freeN(objects);
1728 
1729   return OPERATOR_FINISHED;
1730 }
1731 
UV_OT_reveal(wmOperatorType * ot)1732 static void UV_OT_reveal(wmOperatorType *ot)
1733 {
1734   /* identifiers */
1735   ot->name = "Reveal Hidden";
1736   ot->description = "Reveal all hidden UV vertices";
1737   ot->idname = "UV_OT_reveal";
1738   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1739 
1740   /* api callbacks */
1741   ot->exec = uv_reveal_exec;
1742   ot->poll = ED_operator_uvedit;
1743 
1744   RNA_def_boolean(ot->srna, "select", true, "Select", "");
1745 }
1746 
1747 /** \} */
1748 
1749 /* -------------------------------------------------------------------- */
1750 /** \name Set 2D Cursor Operator
1751  * \{ */
1752 
uv_set_2d_cursor_exec(bContext * C,wmOperator * op)1753 static int uv_set_2d_cursor_exec(bContext *C, wmOperator *op)
1754 {
1755   SpaceImage *sima = CTX_wm_space_image(C);
1756 
1757   if (!sima) {
1758     return OPERATOR_CANCELLED;
1759   }
1760 
1761   RNA_float_get_array(op->ptr, "location", sima->cursor);
1762 
1763   {
1764     struct wmMsgBus *mbus = CTX_wm_message_bus(C);
1765     bScreen *screen = CTX_wm_screen(C);
1766     WM_msg_publish_rna_prop(mbus, &screen->id, sima, SpaceImageEditor, cursor_location);
1767   }
1768 
1769   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
1770 
1771   return OPERATOR_FINISHED;
1772 }
1773 
uv_set_2d_cursor_invoke(bContext * C,wmOperator * op,const wmEvent * event)1774 static int uv_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1775 {
1776   ARegion *region = CTX_wm_region(C);
1777   float location[2];
1778 
1779   if (region->regiontype == RGN_TYPE_WINDOW) {
1780     if (event->mval[1] <= 16) {
1781       SpaceImage *sima = CTX_wm_space_image(C);
1782       if (sima && ED_space_image_show_cache(sima)) {
1783         return OPERATOR_PASS_THROUGH;
1784       }
1785     }
1786   }
1787 
1788   UI_view2d_region_to_view(
1789       &region->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
1790   RNA_float_set_array(op->ptr, "location", location);
1791 
1792   return uv_set_2d_cursor_exec(C, op);
1793 }
1794 
UV_OT_cursor_set(wmOperatorType * ot)1795 static void UV_OT_cursor_set(wmOperatorType *ot)
1796 {
1797   /* identifiers */
1798   ot->name = "Set 2D Cursor";
1799   ot->description = "Set 2D cursor location";
1800   ot->idname = "UV_OT_cursor_set";
1801 
1802   /* api callbacks */
1803   ot->exec = uv_set_2d_cursor_exec;
1804   ot->invoke = uv_set_2d_cursor_invoke;
1805   ot->poll = ED_space_image_cursor_poll;
1806 
1807   /* properties */
1808   RNA_def_float_vector(ot->srna,
1809                        "location",
1810                        2,
1811                        NULL,
1812                        -FLT_MAX,
1813                        FLT_MAX,
1814                        "Location",
1815                        "Cursor location in normalized (0.0-1.0) coordinates",
1816                        -10.0f,
1817                        10.0f);
1818 }
1819 
1820 /** \} */
1821 
1822 /* -------------------------------------------------------------------- */
1823 /** \name Seam from UV Islands Operator
1824  * \{ */
1825 
uv_seams_from_islands_exec(bContext * C,wmOperator * op)1826 static int uv_seams_from_islands_exec(bContext *C, wmOperator *op)
1827 {
1828   Scene *scene = CTX_data_scene(C);
1829   ViewLayer *view_layer = CTX_data_view_layer(C);
1830   const bool mark_seams = RNA_boolean_get(op->ptr, "mark_seams");
1831   const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
1832   bool changed_multi = false;
1833 
1834   uint objects_len = 0;
1835   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1836       view_layer, ((View3D *)NULL), &objects_len);
1837 
1838   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1839     Object *ob = objects[ob_index];
1840     Mesh *me = (Mesh *)ob->data;
1841     BMEditMesh *em = me->edit_mesh;
1842     BMesh *bm = em->bm;
1843     BMIter iter;
1844 
1845     if (!EDBM_uv_check(em)) {
1846       continue;
1847     }
1848 
1849     const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
1850     bool changed = false;
1851 
1852     BMFace *f;
1853     BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1854       if (!uvedit_face_visible_test(scene, f)) {
1855         continue;
1856       }
1857 
1858       BMLoop *l_iter;
1859       BMLoop *l_first;
1860 
1861       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1862       do {
1863         if (l_iter == l_iter->radial_next) {
1864           continue;
1865         }
1866         if (!uvedit_edge_select_test(scene, l_iter, cd_loop_uv_offset)) {
1867           continue;
1868         }
1869 
1870         bool mark = false;
1871         BMLoop *l_other = l_iter->radial_next;
1872         do {
1873           if (!BM_loop_uv_share_edge_check(l_iter, l_other, cd_loop_uv_offset)) {
1874             mark = true;
1875             break;
1876           }
1877         } while ((l_other = l_other->radial_next) != l_iter);
1878 
1879         if (mark) {
1880           if (mark_seams) {
1881             BM_elem_flag_enable(l_iter->e, BM_ELEM_SEAM);
1882           }
1883           if (mark_sharp) {
1884             BM_elem_flag_disable(l_iter->e, BM_ELEM_SMOOTH);
1885           }
1886           changed = true;
1887         }
1888       } while ((l_iter = l_iter->next) != l_first);
1889     }
1890 
1891     if (changed) {
1892       changed_multi = true;
1893       DEG_id_tag_update(&me->id, 0);
1894       WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
1895     }
1896   }
1897   MEM_freeN(objects);
1898 
1899   return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1900 }
1901 
UV_OT_seams_from_islands(wmOperatorType * ot)1902 static void UV_OT_seams_from_islands(wmOperatorType *ot)
1903 {
1904   /* identifiers */
1905   ot->name = "Seams from Islands";
1906   ot->description = "Set mesh seams according to island setup in the UV editor";
1907   ot->idname = "UV_OT_seams_from_islands";
1908 
1909   /* flags */
1910   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1911 
1912   /* api callbacks */
1913   ot->exec = uv_seams_from_islands_exec;
1914   ot->poll = ED_operator_uvedit;
1915 
1916   RNA_def_boolean(ot->srna, "mark_seams", 1, "Mark Seams", "Mark boundary edges as seams");
1917   RNA_def_boolean(ot->srna, "mark_sharp", 0, "Mark Sharp", "Mark boundary edges as sharp");
1918 }
1919 
1920 /** \} */
1921 
1922 /* -------------------------------------------------------------------- */
1923 /** \name Mark Seam Operator
1924  * \{ */
1925 
uv_mark_seam_exec(bContext * C,wmOperator * op)1926 static int uv_mark_seam_exec(bContext *C, wmOperator *op)
1927 {
1928   Scene *scene = CTX_data_scene(C);
1929   ViewLayer *view_layer = CTX_data_view_layer(C);
1930   const ToolSettings *ts = scene->toolsettings;
1931 
1932   BMFace *efa;
1933   BMLoop *loop;
1934   BMIter iter, liter;
1935 
1936   const bool flag_set = !RNA_boolean_get(op->ptr, "clear");
1937   const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
1938 
1939   uint objects_len = 0;
1940   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1941       view_layer, ((View3D *)NULL), &objects_len);
1942 
1943   bool changed = false;
1944 
1945   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1946     Object *ob = objects[ob_index];
1947     Mesh *me = (Mesh *)ob->data;
1948     BMEditMesh *em = me->edit_mesh;
1949     BMesh *bm = em->bm;
1950 
1951     if (synced_selection && (bm->totedgesel == 0)) {
1952       continue;
1953     }
1954 
1955     const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
1956 
1957     BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1958       if (uvedit_face_visible_test(scene, efa)) {
1959         BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) {
1960           if (uvedit_edge_select_test(scene, loop, cd_loop_uv_offset)) {
1961             BM_elem_flag_set(loop->e, BM_ELEM_SEAM, flag_set);
1962             changed = true;
1963           }
1964         }
1965       }
1966     }
1967 
1968     if (changed) {
1969       DEG_id_tag_update(&me->id, 0);
1970       WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
1971     }
1972   }
1973 
1974   if (changed) {
1975     ED_uvedit_live_unwrap(scene, objects, objects_len);
1976   }
1977 
1978   MEM_freeN(objects);
1979 
1980   return OPERATOR_FINISHED;
1981 }
1982 
uv_mark_seam_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))1983 static int uv_mark_seam_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1984 {
1985   uiPopupMenu *pup;
1986   uiLayout *layout;
1987 
1988   if (RNA_struct_property_is_set(op->ptr, "clear")) {
1989     return uv_mark_seam_exec(C, op);
1990   }
1991 
1992   pup = UI_popup_menu_begin(C, IFACE_("Edges"), ICON_NONE);
1993   layout = UI_popup_menu_layout(pup);
1994 
1995   uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
1996   uiItemBooleanO(layout,
1997                  CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Mark Seam"),
1998                  ICON_NONE,
1999                  op->type->idname,
2000                  "clear",
2001                  false);
2002   uiItemBooleanO(layout,
2003                  CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Clear Seam"),
2004                  ICON_NONE,
2005                  op->type->idname,
2006                  "clear",
2007                  true);
2008 
2009   UI_popup_menu_end(C, pup);
2010 
2011   return OPERATOR_INTERFACE;
2012 }
2013 
UV_OT_mark_seam(wmOperatorType * ot)2014 static void UV_OT_mark_seam(wmOperatorType *ot)
2015 {
2016   /* identifiers */
2017   ot->name = "Mark Seam";
2018   ot->description = "Mark selected UV edges as seams";
2019   ot->idname = "UV_OT_mark_seam";
2020 
2021   /* flags */
2022   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2023 
2024   /* api callbacks */
2025   ot->exec = uv_mark_seam_exec;
2026   ot->invoke = uv_mark_seam_invoke;
2027   ot->poll = ED_operator_uvedit;
2028 
2029   RNA_def_boolean(ot->srna, "clear", false, "Clear Seams", "Clear instead of marking seams");
2030 }
2031 
2032 /** \} */
2033 
2034 /* -------------------------------------------------------------------- */
2035 /** \name Operator Registration & Keymap
2036  * \{ */
2037 
ED_operatortypes_uvedit(void)2038 void ED_operatortypes_uvedit(void)
2039 {
2040   /* uvedit_select.c */
2041   WM_operatortype_append(UV_OT_select_all);
2042   WM_operatortype_append(UV_OT_select);
2043   WM_operatortype_append(UV_OT_select_loop);
2044   WM_operatortype_append(UV_OT_select_edge_ring);
2045   WM_operatortype_append(UV_OT_select_linked);
2046   WM_operatortype_append(UV_OT_select_linked_pick);
2047   WM_operatortype_append(UV_OT_select_split);
2048   WM_operatortype_append(UV_OT_select_pinned);
2049   WM_operatortype_append(UV_OT_select_box);
2050   WM_operatortype_append(UV_OT_select_lasso);
2051   WM_operatortype_append(UV_OT_select_circle);
2052   WM_operatortype_append(UV_OT_select_more);
2053   WM_operatortype_append(UV_OT_select_less);
2054   WM_operatortype_append(UV_OT_select_overlap);
2055 
2056   WM_operatortype_append(UV_OT_snap_cursor);
2057   WM_operatortype_append(UV_OT_snap_selected);
2058 
2059   WM_operatortype_append(UV_OT_align);
2060 
2061   WM_operatortype_append(UV_OT_rip);
2062   WM_operatortype_append(UV_OT_stitch);
2063   WM_operatortype_append(UV_OT_shortest_path_pick);
2064   WM_operatortype_append(UV_OT_shortest_path_select);
2065 
2066   WM_operatortype_append(UV_OT_seams_from_islands);
2067   WM_operatortype_append(UV_OT_mark_seam);
2068   WM_operatortype_append(UV_OT_weld);
2069   WM_operatortype_append(UV_OT_remove_doubles);
2070   WM_operatortype_append(UV_OT_pin);
2071 
2072   WM_operatortype_append(UV_OT_average_islands_scale);
2073   WM_operatortype_append(UV_OT_cube_project);
2074   WM_operatortype_append(UV_OT_cylinder_project);
2075   WM_operatortype_append(UV_OT_project_from_view);
2076   WM_operatortype_append(UV_OT_minimize_stretch);
2077   WM_operatortype_append(UV_OT_pack_islands);
2078   WM_operatortype_append(UV_OT_reset);
2079   WM_operatortype_append(UV_OT_sphere_project);
2080   WM_operatortype_append(UV_OT_unwrap);
2081   WM_operatortype_append(UV_OT_smart_project);
2082 
2083   WM_operatortype_append(UV_OT_reveal);
2084   WM_operatortype_append(UV_OT_hide);
2085 
2086   WM_operatortype_append(UV_OT_cursor_set);
2087 }
2088 
ED_operatormacros_uvedit(void)2089 void ED_operatormacros_uvedit(void)
2090 {
2091   wmOperatorType *ot;
2092   wmOperatorTypeMacro *otmacro;
2093 
2094   ot = WM_operatortype_append_macro("UV_OT_rip_move",
2095                                     "UV Rip Move",
2096                                     "Unstitch UV's and move the result",
2097                                     OPTYPE_UNDO | OPTYPE_REGISTER);
2098   WM_operatortype_macro_define(ot, "UV_OT_rip");
2099   otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
2100   RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
2101   RNA_boolean_set(otmacro->ptr, "mirror", false);
2102 }
2103 
ED_keymap_uvedit(wmKeyConfig * keyconf)2104 void ED_keymap_uvedit(wmKeyConfig *keyconf)
2105 {
2106   wmKeyMap *keymap;
2107 
2108   keymap = WM_keymap_ensure(keyconf, "UV Editor", 0, 0);
2109   keymap->poll = ED_operator_uvedit;
2110 }
2111 
2112 /** \} */
2113