style cleanup
[blender-staging.git] / source / blender / editors / uvedit / uvedit_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Antony Riakiotakis.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/uvedit/uvedit_ops.c
29  *  \ingroup eduv
30  */
31
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <math.h>
36 #include <string.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "DNA_object_types.h"
41 #include "DNA_material_types.h"
42 #include "DNA_mesh_types.h"
43 #include "DNA_meshdata_types.h"
44 #include "DNA_node_types.h"
45 #include "DNA_image_types.h"
46 #include "DNA_space_types.h"
47 #include "DNA_scene_types.h"
48
49 #include "BLI_math.h"
50 #include "BLI_lasso.h"
51 #include "BLI_blenlib.h"
52 #include "BLI_array.h"
53 #include "BLI_utildefines.h"
54
55 #include "BKE_context.h"
56 #include "BKE_customdata.h"
57 #include "BKE_depsgraph.h"
58 #include "BKE_image.h"
59 #include "BKE_library.h"
60 #include "BKE_main.h"
61 #include "BKE_material.h"
62 #include "BKE_mesh.h"
63 #include "BKE_node.h"
64 #include "BKE_report.h"
65 #include "BKE_scene.h"
66 #include "BKE_editmesh.h"
67
68 #include "ED_image.h"
69 #include "ED_mesh.h"
70 #include "ED_node.h"
71 #include "ED_uvedit.h"
72 #include "ED_object.h"
73 #include "ED_screen.h"
74 #include "ED_transform.h"
75
76 #include "RNA_access.h"
77 #include "RNA_define.h"
78
79 #include "WM_api.h"
80 #include "WM_types.h"
81
82 #include "UI_view2d.h"
83
84 #include "uvedit_intern.h"
85
86 static void uv_select_all_perform(Scene *scene, Image *ima, BMEditMesh *em, int action);
87 static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object *obedit, bool select);
88 static void uv_select_flush_from_tag_loop(SpaceImage *sima, Scene *scene, Object *obedit, bool select);
89
90 /************************* state testing ************************/
91
92 int ED_uvedit_test(Object *obedit)
93 {
94         BMEditMesh *em;
95         int ret;
96
97         if (!obedit)
98                 return 0;
99         
100         if (obedit->type != OB_MESH)
101                 return 0;
102
103         em = BKE_editmesh_from_object(obedit);
104         ret = EDBM_mtexpoly_check(em);
105         
106         return ret;
107 }
108
109 static int ED_operator_uvedit_can_uv_sculpt(struct bContext *C)
110 {
111         SpaceImage *sima = CTX_wm_space_image(C);
112         ToolSettings *toolsettings = CTX_data_tool_settings(C);
113         Object *obedit = CTX_data_edit_object(C);
114
115         return ED_space_image_show_uvedit(sima, obedit) && !(toolsettings->use_uv_sculpt);
116 }
117
118 static int UNUSED_FUNCTION(ED_operator_uvmap_mesh) (bContext *C)
119 {
120         Object *ob = CTX_data_active_object(C);
121
122         if (ob && ob->type == OB_MESH) {
123                 Mesh *me = ob->data;
124
125                 if (CustomData_get_layer(&me->fdata, CD_MTFACE) != NULL)
126                         return 1;
127         }
128
129         return 0;
130 }
131 /**************************** object active image *****************************/
132
133 static int is_image_texture_node(bNode *node)
134 {
135         return ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT);
136 }
137
138 int ED_object_get_active_image(Object *ob, int mat_nr, Image **ima, ImageUser **iuser, bNode **node_r)
139 {
140         Material *ma = give_current_material(ob, mat_nr);
141         bNode *node = (ma && ma->use_nodes) ? nodeGetActiveTexture(ma->nodetree) : NULL;
142
143         if (node && is_image_texture_node(node)) {
144                 if (ima) *ima = (Image *)node->id;
145                 if (iuser) *iuser = NULL;
146                 if (node_r) *node_r = node;
147                 return TRUE;
148         }
149         
150         if (ima) *ima = NULL;
151         if (iuser) *iuser = NULL;
152         if (node_r) *node_r = node;
153
154         return FALSE;
155 }
156
157 void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima)
158 {
159         Material *ma = give_current_material(ob, mat_nr);
160         bNode *node = (ma && ma->use_nodes) ? nodeGetActiveTexture(ma->nodetree) : NULL;
161
162         if (node && is_image_texture_node(node)) {
163                 node->id = &ima->id;
164                 ED_node_tag_update_nodetree(bmain, ma->nodetree);
165         }
166 }
167
168 /************************* assign image ************************/
169
170 //#define USE_SWITCH_ASPECT
171
172 void ED_uvedit_assign_image(Main *UNUSED(bmain), Scene *scene, Object *obedit, Image *ima, Image *previma)
173 {
174         BMEditMesh *em;
175         BMIter iter;
176         MTexPoly *tf;
177         bool update = false;
178         const bool selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
179         
180         /* skip assigning these procedural images... */
181         if (ima && (ima->type == IMA_TYPE_R_RESULT || ima->type == IMA_TYPE_COMPOSITE))
182                 return;
183
184         /* verify we have a mesh we can work with */
185         if (!obedit || (obedit->type != OB_MESH))
186                 return;
187
188         em = BKE_editmesh_from_object(obedit);
189         if (!em || !em->bm->totface) {
190                 return;
191         }
192
193         if (BKE_scene_use_new_shading_nodes(scene)) {
194                 /* new shading system, do not assign anything */
195         }
196         else {
197                 BMFace *efa;
198
199                 int cd_loop_uv_offset;
200                 int cd_poly_tex_offset;
201
202                 /* old shading system, assign image to selected faces */
203 #ifdef USE_SWITCH_ASPECT
204                 float prev_aspect[2], fprev_aspect;
205                 float aspect[2], faspect;
206
207                 ED_image_get_uv_aspect(previma, prev_aspect, prev_aspect + 1);
208                 ED_image_get_uv_aspect(ima, aspect, aspect + 1);
209
210                 fprev_aspect = prev_aspect[0] / prev_aspect[1];
211                 faspect = aspect[0] / aspect[1];
212 #endif
213
214                 /* ensure we have a uv map */
215                 if (!CustomData_has_layer(&em->bm->pdata, CD_MTEXPOLY)) {
216                         BM_data_layer_add(em->bm, &em->bm->pdata, CD_MTEXPOLY);
217                         BM_data_layer_add(em->bm, &em->bm->ldata, CD_MLOOPUV);
218                         /* make UVs all nice 0-1 */
219                         ED_mesh_uv_loop_reset_ex(obedit->data, CustomData_get_active_layer_index(&em->bm->pdata, CD_MTEXPOLY));
220                         update = true;
221                 }
222
223                 cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
224                 cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
225
226                 /* now assign to all visible faces */
227                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
228                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
229
230                         if (uvedit_face_visible_test(scene, previma, efa, tf) &&
231                             (selected == true || uvedit_face_select_test(scene, efa, cd_loop_uv_offset)))
232                         {
233                                 if (ima) {
234                                         tf->tpage = ima;
235                                         
236                                         if (ima->id.us == 0) id_us_plus(&ima->id);
237                                         else id_lib_extern(&ima->id);
238
239 #ifdef USE_SWITCH_ASPECT
240                                         /* we also need to correct the aspect of uvs */
241                                         if (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) {
242                                                 /* do nothing */
243                                         }
244                                         else {
245                                                 BMIter liter;
246                                                 BMLoop *l;
247
248                                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
249                                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
250
251                                                         luv->uv[0] *= fprev_aspect;
252                                                         luv->uv[0] /= faspect;
253                                                 }
254                                         }
255 #endif
256                                 }
257                                 else {
258                                         tf->tpage = NULL;
259                                 }
260
261                                 update = true;
262                         }
263                 }
264
265                 /* and update depdency graph */
266                 if (update) {
267                         DAG_id_tag_update(obedit->data, 0);
268                 }
269         }
270
271 }
272
273 /* dotile - 1, set the tile flag (from the space image)
274  *          2, set the tile index for the faces. */
275 static bool uvedit_set_tile(Object *obedit, Image *ima, int curtile)
276 {
277         BMEditMesh *em;
278         BMFace *efa;
279         BMIter iter;
280         MTexPoly *tf;
281         int cd_poly_tex_offset;
282         
283         /* verify if we have something to do */
284         if (!ima || !ED_uvedit_test(obedit))
285                 return false;
286
287         if ((ima->tpageflag & IMA_TILES) == 0)
288                 return false;
289
290         /* skip assigning these procedural images... */
291         if (ima->type == IMA_TYPE_R_RESULT || ima->type == IMA_TYPE_COMPOSITE)
292                 return false;
293         
294         em = BKE_editmesh_from_object(obedit);
295
296         cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
297
298         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
299                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
300
301                 if (BM_elem_flag_test(efa, BM_ELEM_SELECT))
302                         tf->tile = curtile;  /* set tile index */
303         }
304
305         DAG_id_tag_update(obedit->data, 0);
306
307         return true;
308 }
309
310 /*********************** space conversion *********************/
311
312 static void uvedit_pixel_to_float(SpaceImage *sima, float *dist, float pixeldist)
313 {
314         int width, height;
315
316         if (sima) {
317                 ED_space_image_get_size(sima, &width, &height);
318         }
319         else {
320                 width =  IMG_SIZE_FALLBACK;
321                 height = IMG_SIZE_FALLBACK;
322         }
323
324         dist[0] = pixeldist / width;
325         dist[1] = pixeldist / height;
326 }
327
328 /*************** visibility and selection utilities **************/
329
330 bool uvedit_face_visible_nolocal(Scene *scene, BMFace *efa)
331 {
332         ToolSettings *ts = scene->toolsettings;
333
334         if (ts->uv_flag & UV_SYNC_SELECTION)
335                 return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0);
336         else
337                 return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT));
338 }
339
340 bool uvedit_face_visible_test(Scene *scene, Image *ima, BMFace *efa, MTexPoly *tf)
341 {
342         ToolSettings *ts = scene->toolsettings;
343
344         if (ts->uv_flag & UV_SHOW_SAME_IMAGE)
345                 return (tf->tpage == ima) ? uvedit_face_visible_nolocal(scene, efa) : false;
346         else
347                 return uvedit_face_visible_nolocal(scene, efa);
348 }
349
350 bool uvedit_face_select_test(Scene *scene, BMFace *efa,
351                              const int cd_loop_uv_offset)
352 {
353         ToolSettings *ts = scene->toolsettings;
354         if (ts->uv_flag & UV_SYNC_SELECTION) {
355                 return (BM_elem_flag_test(efa, BM_ELEM_SELECT));
356         }
357         else {
358                 BMLoop *l;
359                 MLoopUV *luv;
360                 BMIter liter;
361
362                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
363                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
364                         if (!(luv->flag & MLOOPUV_VERTSEL))
365                                 return false;
366                 }
367
368                 return true;
369         }
370 }
371
372 bool uvedit_face_select_set(struct Scene *scene, struct BMEditMesh *em, struct BMFace *efa, const bool select,
373                             const bool do_history, const int cd_loop_uv_offset)
374 {
375         if (select) {
376                 return uvedit_face_select_enable(scene, em, efa, do_history, cd_loop_uv_offset);
377         }
378         else {
379                 return uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
380         }
381 }
382
383 bool uvedit_face_select_enable(Scene *scene, BMEditMesh *em, BMFace *efa, const bool do_history,
384                                const int cd_loop_uv_offset)
385 {
386         ToolSettings *ts = scene->toolsettings;
387
388         if (ts->uv_flag & UV_SYNC_SELECTION) {
389                 BM_face_select_set(em->bm, efa, TRUE);
390                 if (do_history) {
391                         BM_select_history_store(em->bm, (BMElem *)efa);
392                 }
393         }
394         else {
395                 BMLoop *l;
396                 MLoopUV *luv;
397                 BMIter liter;
398
399                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
400                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
401                         luv->flag |= MLOOPUV_VERTSEL;
402                 }
403
404                 return true;
405         }
406
407         return false;
408 }
409
410 bool uvedit_face_select_disable(Scene *scene, BMEditMesh *em, BMFace *efa,
411                                 const int cd_loop_uv_offset)
412 {
413         ToolSettings *ts = scene->toolsettings;
414
415         if (ts->uv_flag & UV_SYNC_SELECTION) {
416                 BM_face_select_set(em->bm, efa, FALSE);
417         }
418         else {
419                 BMLoop *l;
420                 MLoopUV *luv;
421                 BMIter liter;
422
423                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
424                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
425                         luv->flag &= ~MLOOPUV_VERTSEL;
426                 }
427
428                 return true;
429         }
430
431         return false;
432 }
433
434 bool uvedit_edge_select_test(Scene *scene, BMLoop *l,
435                              const int cd_loop_uv_offset)
436 {
437         ToolSettings *ts = scene->toolsettings;
438
439         if (ts->uv_flag & UV_SYNC_SELECTION) {
440                 if (ts->selectmode & SCE_SELECT_FACE) {
441                         return BM_elem_flag_test(l->f, BM_ELEM_SELECT);
442                 }
443                 else if (ts->selectmode == SCE_SELECT_EDGE) {
444                         return BM_elem_flag_test(l->e, BM_ELEM_SELECT);
445                 }
446                 else {
447                         return BM_elem_flag_test(l->v, BM_ELEM_SELECT) && 
448                                BM_elem_flag_test(l->next->v, BM_ELEM_SELECT);
449                 }
450         }
451         else {
452                 MLoopUV *luv1, *luv2;
453
454                 luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
455                 luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
456
457                 return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL);
458         }
459 }
460
461 void uvedit_edge_select_set(BMEditMesh *em, Scene *scene, BMLoop *l, const bool select,
462                             const bool do_history, const int cd_loop_uv_offset)
463
464 {
465         if (select) {
466                 uvedit_edge_select_enable(em, scene, l, do_history, cd_loop_uv_offset);
467         }
468         else {
469                 uvedit_edge_select_disable(em, scene, l, cd_loop_uv_offset);
470         }
471 }
472
473 void uvedit_edge_select_enable(BMEditMesh *em, Scene *scene, BMLoop *l, const bool do_history,
474                                const int cd_loop_uv_offset)
475
476 {
477         ToolSettings *ts = scene->toolsettings;
478
479         if (ts->uv_flag & UV_SYNC_SELECTION) {
480                 if (ts->selectmode & SCE_SELECT_FACE)
481                         BM_face_select_set(em->bm, l->f, TRUE);
482                 else if (ts->selectmode & SCE_SELECT_EDGE)
483                         BM_edge_select_set(em->bm, l->e, TRUE);
484                 else {
485                         BM_vert_select_set(em->bm, l->e->v1, TRUE);
486                         BM_vert_select_set(em->bm, l->e->v2, TRUE);
487                 }
488
489                 if (do_history) {
490                         BM_select_history_store(em->bm, (BMElem *)l->e);
491                 }
492         }
493         else {
494                 MLoopUV *luv1, *luv2;
495
496                 luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
497                 luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
498                 
499                 luv1->flag |= MLOOPUV_VERTSEL;
500                 luv2->flag |= MLOOPUV_VERTSEL;
501         }
502 }
503
504 void uvedit_edge_select_disable(BMEditMesh *em, Scene *scene, BMLoop *l,
505                                 const int cd_loop_uv_offset)
506
507 {
508         ToolSettings *ts = scene->toolsettings;
509
510         if (ts->uv_flag & UV_SYNC_SELECTION) {
511                 if (ts->selectmode & SCE_SELECT_FACE)
512                         BM_face_select_set(em->bm, l->f, FALSE);
513                 else if (ts->selectmode & SCE_SELECT_EDGE)
514                         BM_edge_select_set(em->bm, l->e, FALSE);
515                 else {
516                         BM_vert_select_set(em->bm, l->e->v1, FALSE);
517                         BM_vert_select_set(em->bm, l->e->v2, FALSE);
518                 }
519         }
520         else {
521                 MLoopUV *luv1, *luv2;
522
523                 luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
524                 luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
525                 
526                 luv1->flag &= ~MLOOPUV_VERTSEL;
527                 luv2->flag &= ~MLOOPUV_VERTSEL;
528         }
529 }
530
531 bool uvedit_uv_select_test(Scene *scene, BMLoop *l,
532                            const int cd_loop_uv_offset)
533 {
534         ToolSettings *ts = scene->toolsettings;
535
536         if (ts->uv_flag & UV_SYNC_SELECTION) {
537                 if (ts->selectmode & SCE_SELECT_FACE)
538                         return BM_elem_flag_test_bool(l->f, BM_ELEM_SELECT);
539                 else
540                         return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT);
541         }
542         else {
543                 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
544                 return (luv->flag & MLOOPUV_VERTSEL) != 0;
545         }
546 }
547
548 void uvedit_uv_select_set(BMEditMesh *em, Scene *scene, BMLoop *l, const bool select,
549                           const bool do_history, const int cd_loop_uv_offset)
550 {
551         if (select) {
552                 uvedit_uv_select_enable(em, scene, l, do_history, cd_loop_uv_offset);
553         }
554         else {
555                 uvedit_uv_select_disable(em, scene, l, cd_loop_uv_offset);
556         }
557 }
558
559 void uvedit_uv_select_enable(BMEditMesh *em, Scene *scene, BMLoop *l,
560                              const bool do_history, const int cd_loop_uv_offset)
561 {
562         ToolSettings *ts = scene->toolsettings;
563
564         if (ts->uv_flag & UV_SYNC_SELECTION) {
565                 if (ts->selectmode & SCE_SELECT_FACE)
566                         BM_face_select_set(em->bm, l->f, TRUE);
567                 else
568                         BM_vert_select_set(em->bm, l->v, TRUE);
569
570                 if (do_history) {
571                         BM_select_history_remove(em->bm, (BMElem *)l->v);
572                 }
573         }
574         else {
575                 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
576                 luv->flag |= MLOOPUV_VERTSEL;
577         }
578 }
579
580 void uvedit_uv_select_disable(BMEditMesh *em, Scene *scene, BMLoop *l,
581                               const int cd_loop_uv_offset)
582 {
583         ToolSettings *ts = scene->toolsettings;
584
585         if (ts->uv_flag & UV_SYNC_SELECTION) {
586                 if (ts->selectmode & SCE_SELECT_FACE)
587                         BM_face_select_set(em->bm, l->f, FALSE);
588                 else
589                         BM_vert_select_set(em->bm, l->v, FALSE);
590         }
591         else {
592                 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
593                 luv->flag &= ~MLOOPUV_VERTSEL;
594         }
595 }
596
597 /*********************** live unwrap utilities ***********************/
598
599 void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
600 {
601         if (sima && (sima->flag & SI_LIVE_UNWRAP)) {
602                 ED_uvedit_live_unwrap_begin(scene, obedit);
603                 ED_uvedit_live_unwrap_re_solve();
604                 ED_uvedit_live_unwrap_end(0);
605         }
606 }
607
608 /*********************** geometric utilities ***********************/
609 void uv_poly_center(BMFace *f, float r_cent[2], const int cd_loop_uv_offset)
610 {
611         BMLoop *l;
612         MLoopUV *luv;
613         BMIter liter;
614
615         zero_v2(r_cent);
616
617         BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
618                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
619                 add_v2_v2(r_cent, luv->uv);
620         }
621
622         mul_v2_fl(r_cent, 1.0f / (float)f->len);
623 }
624
625 void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len)
626 {
627         int i;
628         for (i = 0; i < len; i++) {
629                 uv[i][0] = uv_orig[i][0] * aspx;
630                 uv[i][1] = uv_orig[i][1] * aspy;
631         }
632 }
633
634 bool ED_uvedit_minmax(Scene *scene, Image *ima, Object *obedit, float r_min[2], float r_max[2])
635 {
636         BMEditMesh *em = BKE_editmesh_from_object(obedit);
637         BMFace *efa;
638         BMLoop *l;
639         BMIter iter, liter;
640         MTexPoly *tf;
641         MLoopUV *luv;
642         bool change = false;
643
644         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
645         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
646
647         INIT_MINMAX2(r_min, r_max);
648
649         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
650                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
651                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
652                         continue;
653                 
654                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
655                         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
656                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
657                                 minmax_v2v2_v2(r_min, r_max, luv->uv);
658                                 change = true;
659                         }
660                 }
661         }
662
663         return change;
664 }
665
666 static bool ED_uvedit_median(Scene *scene, Image *ima, Object *obedit, float co[2])
667 {
668         BMEditMesh *em = BKE_editmesh_from_object(obedit);
669         BMFace *efa;
670         BMLoop *l;
671         BMIter iter, liter;
672         MTexPoly *tf;
673         MLoopUV *luv;
674         unsigned int sel = 0;
675
676         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
677         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
678
679         zero_v2(co);
680         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
681                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
682                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
683                         continue;
684                 
685                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
686                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
687                         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
688                                 add_v2_v2(co, luv->uv);
689                                 sel++;
690                         }
691                 }
692         }
693
694         mul_v2_fl(co, 1.0f / (float)sel);
695
696         return (sel != 0);
697 }
698
699 static bool uvedit_center(Scene *scene, Image *ima, Object *obedit, float cent[2], char mode)
700 {
701         bool change = false;
702         
703         if (mode == V3D_CENTER) {  /* bounding box */
704                 float min[2], max[2];
705                 if (ED_uvedit_minmax(scene, ima, obedit, min, max)) {
706                         mid_v2_v2v2(cent, min, max);
707                         change = true;
708                 }
709         }
710         else {
711                 if (ED_uvedit_median(scene, ima, obedit, cent)) {
712                         change = true;
713                 }
714         }
715
716         return change;
717 }
718
719 /************************** find nearest ****************************/
720
721 void uv_find_nearest_edge(Scene *scene, Image *ima, BMEditMesh *em, const float co[2], NearestHit *hit)
722 {
723         MTexPoly *tf;
724         BMFace *efa;
725         BMLoop *l;
726         BMIter iter, liter;
727         MLoopUV *luv, *luv_next;
728         float mindist_squared, dist_squared;
729         int i;
730
731         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
732         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
733
734         mindist_squared = 1e10f;
735         memset(hit, 0, sizeof(*hit));
736
737         BM_mesh_elem_index_ensure(em->bm, BM_VERT);
738         
739         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
740                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
741                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
742                         continue;
743                 
744                 i = 0;
745                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
746                         luv      = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
747                         luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
748
749                         dist_squared = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
750
751                         if (dist_squared < mindist_squared) {
752                                 hit->tf = tf;
753                                 hit->efa = efa;
754                                 
755                                 hit->l = l;
756                                 hit->nextl = l->next;
757                                 hit->luv = luv;
758                                 hit->luv_next = luv_next;
759                                 hit->lindex = i;
760                                 hit->vert1 = BM_elem_index_get(hit->l->v);
761                                 hit->vert2 = BM_elem_index_get(hit->l->next->v);
762
763                                 mindist_squared = dist_squared;
764                         }
765
766                         i++;
767                 }
768         }
769 }
770
771 static void uv_find_nearest_face(Scene *scene, Image *ima, BMEditMesh *em, const float co[2], NearestHit *hit)
772 {
773         MTexPoly *tf;
774         BMFace *efa;
775         BMIter iter;
776         float mindist, dist, cent[2];
777
778         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
779         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
780
781         mindist = 1e10f;
782         memset(hit, 0, sizeof(*hit));
783
784         /*this will fill in hit.vert1 and hit.vert2*/
785         uv_find_nearest_edge(scene, ima, em, co, hit);
786         hit->l = hit->nextl = NULL;
787         hit->luv = hit->luv_next = NULL;
788
789         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
790                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
791                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
792                         continue;
793
794                 uv_poly_center(efa, cent, cd_loop_uv_offset);
795
796                 dist = fabsf(co[0] - cent[0]) + fabsf(co[1] - cent[1]);
797
798                 if (dist < mindist) {
799                         hit->tf = tf;
800                         hit->efa = efa;
801                         mindist = dist;
802                 }
803         }
804 }
805
806 static int uv_nearest_between(BMEditMesh *em, BMFace *efa, int UNUSED(nverts), int id,
807                               const float co[2], const float uv[2])
808 {
809         BMLoop *l;
810         MLoopUV *luv;
811         BMIter iter;
812         float m[2], v1[2], v2[2], c1, c2, *uv1 = NULL, /* *uv2, */ /* UNUSED */ *uv3 = NULL;
813         int id1, id2, i;
814         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
815
816         id1 = (id + efa->len - 1) % efa->len;
817         id2 = (id + efa->len + 1) % efa->len;
818
819         sub_v2_v2v2(m, co, uv);
820
821         i = 0;
822         BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
823                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
824                 
825                 if (i == id1) {
826                         uv1 = luv->uv;
827                 }
828                 else if (i == id) {
829                         /* uv2 = luv->uv; */ /* UNUSED */
830                 }
831                 else if (i == id2) {
832                         uv3 = luv->uv;
833                 }
834                 i++;
835         }
836
837         sub_v2_v2v2(v1, uv1, uv);
838         sub_v2_v2v2(v2, uv3, uv);
839
840         /* m and v2 on same side of v-v1? */
841         c1 = v1[0] * m[1] - v1[1] * m[0];
842         c2 = v1[0] * v2[1] - v1[1] * v2[0];
843
844         if (c1 * c2 < 0.0f)
845                 return 0;
846
847         /* m and v1 on same side of v-v2? */
848         c1 = v2[0] * m[1] - v2[1] * m[0];
849         c2 = v2[0] * v1[1] - v2[1] * v1[0];
850
851         return (c1 * c2 >= 0.0f);
852 }
853
854 void uv_find_nearest_vert(Scene *scene, Image *ima, BMEditMesh *em,
855                           float const co[2], const float penalty[2], NearestHit *hit)
856 {
857         BMFace *efa;
858         BMLoop *l;
859         BMIter iter, liter;
860         MTexPoly *tf;
861         MLoopUV *luv;
862         float mindist, dist;
863         int i;
864
865         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
866         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
867
868         /*this will fill in hit.vert1 and hit.vert2*/
869         uv_find_nearest_edge(scene, ima, em, co, hit);
870         hit->l = hit->nextl = NULL;
871         hit->luv = hit->luv_next = NULL;
872
873         mindist = 1e10f;
874         memset(hit, 0, sizeof(*hit));
875         
876         BM_mesh_elem_index_ensure(em->bm, BM_VERT);
877
878         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
879                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
880                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
881                         continue;
882                 
883                 i = 0;
884                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
885                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
886
887                         if (penalty && uvedit_uv_select_test(scene, l, cd_loop_uv_offset))
888                                 dist = fabsf(co[0] - luv->uv[0]) + penalty[0] + fabsf(co[1] - luv->uv[1]) + penalty[1];
889                         else
890                                 dist = fabsf(co[0] - luv->uv[0]) + fabsf(co[1] - luv->uv[1]);
891
892                         if (dist <= mindist) {
893                                 if (dist == mindist)
894                                         if (!uv_nearest_between(em, efa, efa->len, i, co, luv->uv)) {
895                                                 i++;
896                                                 continue;
897                                         }
898
899                                 mindist = dist;
900
901                                 hit->l = l;
902                                 hit->nextl = l->next;
903                                 hit->luv = luv;
904                                 hit->luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
905                                 hit->tf = tf;
906                                 hit->efa = efa;
907                                 hit->lindex = i;
908                                 hit->vert1 = BM_elem_index_get(hit->l->v);
909                         }
910
911                         i++;
912                 }
913         }
914 }
915
916 int ED_uvedit_nearest_uv(Scene *scene, Object *obedit, Image *ima, const float co[2], float r_uv[2])
917 {
918         BMEditMesh *em = BKE_editmesh_from_object(obedit);
919         BMFace *efa;
920         BMLoop *l;
921         BMIter iter, liter;
922         MTexPoly *tf;
923         MLoopUV *luv;
924         float mindist, dist;
925         int found = FALSE;
926
927         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
928         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
929
930         mindist = 1e10f;
931         copy_v2_v2(r_uv, co);
932         
933         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
934                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
935                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
936                         continue;
937                 
938                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
939                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
940                         dist = fabsf(co[0] - luv->uv[0]) + fabsf(co[1] - luv->uv[1]);
941
942                         if (dist <= mindist) {
943                                 mindist = dist;
944
945                                 copy_v2_v2(r_uv, luv->uv);
946                                 found = TRUE;
947                         }
948                 }
949         }
950
951         return found;
952 }
953
954 /*********************** loop select ***********************/
955
956 static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first)
957 {
958         UvMapVert *iterv;
959         int count = 0;
960
961         for (iterv = first; iterv; iterv = iterv->next) {
962                 if (iterv->separate && iterv != first)
963                         break;
964
965                 count++;
966         }
967         
968         if (count < 5)
969                 first->flag = 1;
970 }
971
972 static UvMapVert *uv_select_edgeloop_vertex_map_get(UvVertMap *vmap, BMFace *efa, int a)
973 {
974         UvMapVert *iterv, *first;
975         BMLoop *l;
976
977         l = BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, a);
978         first = EDBM_uv_vert_map_at_index(vmap,  BM_elem_index_get(l->v));
979
980         for (iterv = first; iterv; iterv = iterv->next) {
981                 if (iterv->separate)
982                         first = iterv;
983                 if (iterv->f == BM_elem_index_get(efa))
984                         return first;
985         }
986         
987         return NULL;
988 }
989
990 static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, UvMapVert *first1, UvMapVert *first2, int *totface)
991 {
992         UvMapVert *iterv1, *iterv2;
993         BMFace *efa;
994         int tot = 0;
995
996         /* count number of faces this edge has */
997         for (iterv1 = first1; iterv1; iterv1 = iterv1->next) {
998                 if (iterv1->separate && iterv1 != first1)
999                         break;
1000
1001                 for (iterv2 = first2; iterv2; iterv2 = iterv2->next) {
1002                         if (iterv2->separate && iterv2 != first2)
1003                                 break;
1004
1005                         if (iterv1->f == iterv2->f) {
1006                                 /* if face already tagged, don't do this edge */
1007                                 efa = EDBM_face_at_index(em, iterv1->f);
1008                                 if (BM_elem_flag_test(efa, BM_ELEM_TAG))
1009                                         return false;
1010
1011                                 tot++;
1012                                 break;
1013                         }
1014                 }
1015         }
1016
1017         if (*totface == 0) /* start edge */
1018                 *totface = tot;
1019         else if (tot != *totface) /* check for same number of faces as start edge */
1020                 return false;
1021
1022         /* tag the faces */
1023         for (iterv1 = first1; iterv1; iterv1 = iterv1->next) {
1024                 if (iterv1->separate && iterv1 != first1)
1025                         break;
1026
1027                 for (iterv2 = first2; iterv2; iterv2 = iterv2->next) {
1028                         if (iterv2->separate && iterv2 != first2)
1029                                 break;
1030
1031                         if (iterv1->f == iterv2->f) {
1032                                 efa = EDBM_face_at_index(em, iterv1->f);
1033                                 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1034                                 break;
1035                         }
1036                 }
1037         }
1038
1039         return true;
1040 }
1041
1042 static int uv_select_edgeloop(Scene *scene, Image *ima, BMEditMesh *em, NearestHit *hit,
1043                               float limit[2], const bool extend)
1044 {
1045         BMFace *efa;
1046         BMIter iter, liter;
1047         BMLoop *l;
1048         MTexPoly *tf;
1049         UvVertMap *vmap;
1050         UvMapVert *iterv1, *iterv2;
1051         int a, looking, nverts, starttotf, select;
1052
1053         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1054         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1055
1056         /* setup */
1057         EDBM_index_arrays_ensure(em, BM_FACE);
1058         vmap = EDBM_uv_vert_map_create(em, 0, limit);
1059
1060         BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE);
1061
1062         if (!extend) {
1063                 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
1064         }
1065
1066         BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, FALSE);
1067
1068         /* set flags for first face and verts */
1069         nverts = hit->efa->len;
1070         iterv1 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->lindex);
1071         iterv2 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, (hit->lindex + 1) % nverts);
1072         uv_select_edgeloop_vertex_loop_flag(iterv1);
1073         uv_select_edgeloop_vertex_loop_flag(iterv2);
1074
1075         starttotf = 0;
1076         uv_select_edgeloop_edge_tag_faces(em, iterv1, iterv2, &starttotf);
1077
1078         /* sorry, first edge isn't even ok */
1079         if (iterv1->flag == 0 && iterv2->flag == 0) looking = 0;
1080         else looking = 1;
1081
1082         /* iterate */
1083         while (looking) {
1084                 looking = 0;
1085
1086                 /* find correct valence edges which are not tagged yet, but connect to tagged one */
1087
1088                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1089                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1090
1091                         if (!BM_elem_flag_test(efa, BM_ELEM_TAG) && uvedit_face_visible_test(scene, ima, efa, tf)) {
1092                                 nverts = efa->len;
1093                                 for (a = 0; a < nverts; a++) {
1094                                         /* check face not hidden and not tagged */
1095                                         iterv1 = uv_select_edgeloop_vertex_map_get(vmap, efa, a);
1096                                         iterv2 = uv_select_edgeloop_vertex_map_get(vmap, efa, (a + 1) % nverts);
1097                                         
1098                                         if (!iterv1 || !iterv2)
1099                                                 continue;
1100
1101                                         /* check if vertex is tagged and has right valence */
1102                                         if (iterv1->flag || iterv2->flag) {
1103                                                 if (uv_select_edgeloop_edge_tag_faces(em, iterv1, iterv2, &starttotf)) {
1104                                                         looking = 1;
1105                                                         BM_elem_flag_enable(efa, BM_ELEM_TAG);
1106
1107                                                         uv_select_edgeloop_vertex_loop_flag(iterv1);
1108                                                         uv_select_edgeloop_vertex_loop_flag(iterv2);
1109                                                         break;
1110                                                 }
1111                                         }
1112                                 }
1113                         }
1114                 }
1115         }
1116
1117         /* do the actual select/deselect */
1118         nverts = hit->efa->len;
1119         iterv1 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->lindex);
1120         iterv2 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, (hit->lindex + 1) % nverts);
1121         iterv1->flag = 1;
1122         iterv2->flag = 1;
1123
1124         if (extend) {
1125                 if (uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset))
1126                         select = 0;
1127                 else
1128                         select = 1;
1129         }
1130         else
1131                 select = 1;
1132         
1133         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1134                 a = 0;
1135                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1136                         iterv1 = uv_select_edgeloop_vertex_map_get(vmap, efa, a);
1137
1138                         if (iterv1->flag) {
1139                                 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
1140                         }
1141
1142                         a++;
1143                 }
1144         }
1145
1146         /* cleanup */
1147         EDBM_uv_vert_map_free(vmap);
1148
1149         return (select) ? 1 : -1;
1150 }
1151
1152 /*********************** linked select ***********************/
1153
1154 static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const float limit[2], NearestHit *hit, bool extend)
1155 {
1156         BMFace *efa;
1157         BMLoop *l;
1158         BMIter iter, liter;
1159         MTexPoly *tf;
1160         MLoopUV *luv;
1161         UvVertMap *vmap;
1162         UvMapVert *vlist, *iterv, *startv;
1163         int i, stacksize = 0, *stack;
1164         unsigned int a;
1165         char *flag;
1166
1167         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1168         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1169
1170         EDBM_index_arrays_ensure(em, BM_FACE); /* we can use this too */
1171         vmap = EDBM_uv_vert_map_create(em, 1, limit);
1172
1173         if (vmap == NULL)
1174                 return;
1175
1176         stack = MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack");
1177         flag = MEM_callocN(sizeof(*flag) * em->bm->totface, "UvLinkFlag");
1178
1179         if (!hit) {
1180                 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1181                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1182
1183                         if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1184                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1185                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1186
1187                                         if (luv->flag & MLOOPUV_VERTSEL) {
1188                                                 stack[stacksize] = a;
1189                                                 stacksize++;
1190                                                 flag[a] = 1;
1191
1192                                                 break;
1193                                         }
1194                                 }
1195                         }
1196                 }
1197         }
1198         else {
1199                 a = 0;
1200                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1201                         if (efa == hit->efa) {
1202                                 stack[stacksize] = a;
1203                                 stacksize++;
1204                                 flag[a] = 1;
1205                                 break;
1206                         }
1207
1208                         a++;
1209                 }
1210         }
1211
1212         while (stacksize > 0) {
1213                 int j;
1214
1215                 stacksize--;
1216                 a = stack[stacksize];
1217                 
1218                 j = 0;
1219                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1220                         if (j == a)
1221                                 break;
1222
1223                         j++;
1224                 }
1225
1226                 i = 0;
1227                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1228
1229                         /* make_uv_vert_map_EM sets verts tmp.l to the indices */
1230                         vlist = EDBM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
1231                         
1232                         startv = vlist;
1233
1234                         for (iterv = vlist; iterv; iterv = iterv->next) {
1235                                 if (iterv->separate)
1236                                         startv = iterv;
1237                                 if (iterv->f == a)
1238                                         break;
1239                         }
1240
1241                         for (iterv = startv; iterv; iterv = iterv->next) {
1242                                 if ((startv != iterv) && (iterv->separate))
1243                                         break;
1244                                 else if (!flag[iterv->f]) {
1245                                         flag[iterv->f] = 1;
1246                                         stack[stacksize] = iterv->f;
1247                                         stacksize++;
1248                                 }
1249                         }
1250
1251                         i++;
1252                 }
1253         }
1254
1255         if (!extend) {
1256                 a = 0;
1257                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1258                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1259                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1260                                 
1261                                 if (flag[a])
1262                                         luv->flag |= MLOOPUV_VERTSEL;
1263                                 else
1264                                         luv->flag &= ~MLOOPUV_VERTSEL;
1265                         }
1266                         a++;
1267                 }
1268         }
1269         else {
1270                 a = 0;
1271                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1272                         if (!flag[a]) {
1273                                 a++;
1274                                 continue;
1275                         }
1276                         
1277                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1278                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1279                                                 
1280                                 if (luv->flag & MLOOPUV_VERTSEL)
1281                                         break;
1282                         }
1283                         
1284                         if (l)
1285                                 break;
1286                         
1287                         a++;
1288                 }
1289
1290                 if (efa) {
1291                         a = 0;
1292                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1293                                 if (!flag[a]) {
1294                                         a++;
1295                                         continue;
1296                                 }
1297
1298                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1299                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1300                                         
1301                                         luv->flag &= ~MLOOPUV_VERTSEL;
1302                                 }
1303
1304                                 a++;
1305                         }
1306                 }
1307                 else {
1308                         a = 0;
1309                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1310                                 if (!flag[a]) {
1311                                         a++;
1312                                         continue;
1313                                 }
1314
1315                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1316                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1317                                         
1318                                         luv->flag |= MLOOPUV_VERTSEL;
1319                                 }
1320
1321                                 a++;
1322                         }
1323                 }
1324         }
1325         
1326         MEM_freeN(stack);
1327         MEM_freeN(flag);
1328         EDBM_uv_vert_map_free(vmap);
1329 }
1330
1331 /* WATCH IT: this returns first selected UV,
1332  * not ideal in many cases since there could be multiple */
1333 static float *uv_sel_co_from_eve(Scene *scene, Image *ima, BMEditMesh *em, BMVert *eve)
1334 {
1335         BMIter liter;
1336         BMLoop *l;
1337
1338         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1339         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1340
1341         BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
1342                 MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1343
1344                 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1345                         continue;
1346
1347                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1348                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1349                         return luv->uv;
1350                 }
1351         }
1352
1353         return NULL;
1354 }
1355
1356 static int uv_select_more_less(bContext *C, const bool select)
1357 {
1358         Scene *scene = CTX_data_scene(C);
1359         Object *obedit = CTX_data_edit_object(C);
1360         Image *ima = CTX_data_edit_image(C);
1361         SpaceImage *sima = CTX_wm_space_image(C);
1362         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1363
1364         BMFace *efa;
1365         BMLoop *l;
1366         BMIter iter, liter;
1367         ToolSettings *ts = scene->toolsettings;
1368
1369         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1370         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1371
1372         if (ts->uv_flag & UV_SYNC_SELECTION) {
1373                 if (select) {
1374                         EDBM_select_more(em);
1375                 }
1376                 else {
1377                         EDBM_select_less(em);
1378                 }
1379
1380                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1381                 return OPERATOR_FINISHED;
1382         }
1383
1384         if (ts->uv_selectmode == UV_SELECT_FACE) {
1385
1386                 /* clear tags */
1387                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1388                         BM_elem_flag_disable(efa, BM_ELEM_TAG);
1389                 }
1390
1391                 /* mark loops to be selected */
1392                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1393                         MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1394
1395                         if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1396
1397 #define IS_SEL   1
1398 #define IS_UNSEL 2
1399
1400                                 int sel_state = 0;
1401
1402                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1403                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1404                                         if (luv->flag & MLOOPUV_VERTSEL) {
1405                                                 sel_state |= IS_SEL;
1406                                         }
1407                                         else {
1408                                                 sel_state |= IS_UNSEL;
1409                                         }
1410
1411                                         /* if we have a mixed selection, tag to grow it */
1412                                         if (sel_state == (IS_SEL | IS_UNSEL)) {
1413                                                 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1414                                                 break;
1415                                         }
1416                                 }
1417
1418 #undef IS_SEL
1419 #undef IS_UNSEL
1420
1421                         }
1422                 }
1423
1424                 /* select tagged faces */
1425                 uv_select_flush_from_tag_face(sima, scene, obedit, select);
1426         }
1427         else {
1428
1429                 /* clear tags */
1430                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1431                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1432                                 BM_elem_flag_disable(l, BM_ELEM_TAG);
1433                         }
1434                 }
1435
1436                 /* mark loops to be selected */
1437                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1438                         MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1439
1440                         if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1441                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1442
1443                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1444
1445                                         if (((luv->flag & MLOOPUV_VERTSEL) != 0) == select) {
1446                                                 BM_elem_flag_enable(l->next, BM_ELEM_TAG);
1447                                                 BM_elem_flag_enable(l->prev, BM_ELEM_TAG);
1448                                         }
1449                                 }
1450                         }
1451                 }
1452
1453                 /* select tagged loops */
1454                 uv_select_flush_from_tag_loop(sima, scene, obedit, select);
1455         }
1456
1457         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1458
1459         return OPERATOR_FINISHED;
1460 }
1461
1462 static int uv_select_more_exec(bContext *C, wmOperator *UNUSED(op))
1463 {
1464         return uv_select_more_less(C, true);
1465 }
1466
1467 static void UV_OT_select_more(wmOperatorType *ot)
1468 {
1469         /* identifiers */
1470         ot->name = "Select More";
1471         ot->description = "Select more UV vertices connected to initial selection";
1472         ot->idname = "UV_OT_select_more";
1473         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1474
1475         /* api callbacks */
1476         ot->exec = uv_select_more_exec;
1477         ot->poll = ED_operator_uvedit_space_image;
1478 }
1479
1480 static int uv_select_less_exec(bContext *C, wmOperator *UNUSED(op))
1481 {
1482         return uv_select_more_less(C, false);
1483 }
1484
1485 static void UV_OT_select_less(wmOperatorType *ot)
1486 {
1487         /* identifiers */
1488         ot->name = "Select Less";
1489         ot->description = "Deselect UV vertices at the boundary of each selection region";
1490         ot->idname = "UV_OT_select_less";
1491         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1492
1493         /* api callbacks */
1494         ot->exec = uv_select_less_exec;
1495         ot->poll = ED_operator_uvedit_space_image;
1496 }
1497
1498 /* ******************** align operator **************** */
1499
1500 static void uv_weld_align(bContext *C, int tool)
1501 {
1502         Object *obedit = CTX_data_edit_object(C);
1503         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1504         SpaceImage *sima;
1505         Scene *scene;
1506         Image *ima;
1507         MTexPoly *tf;
1508         float cent[2], min[2], max[2];
1509
1510         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1511         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1512
1513         scene = CTX_data_scene(C);
1514         ima = CTX_data_edit_image(C);
1515         sima = CTX_wm_space_image(C);
1516
1517         INIT_MINMAX2(min, max);
1518
1519         if (tool == 'a') {
1520                 BMIter iter, liter;
1521                 BMFace *efa;
1522                 BMLoop *l;
1523
1524                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1525                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1526
1527                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1528                                 continue;
1529
1530                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1531                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1532                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1533                                         minmax_v2v2_v2(min, max, luv->uv);
1534                                 }
1535                         }
1536                 }
1537
1538                 tool = (max[0] - min[0] >= max[1] - min[1]) ? 'y' : 'x';
1539         }
1540
1541         uvedit_center(scene, ima, obedit, cent, 0);
1542
1543         if (tool == 'x' || tool == 'w') {
1544                 BMIter iter, liter;
1545                 BMFace *efa;
1546                 BMLoop *l;
1547
1548                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1549                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1550                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1551                                 continue;
1552
1553                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1554                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1555                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1556                                         luv->uv[0] = cent[0];
1557                                 }
1558
1559                         }
1560                 }
1561         }
1562
1563         if (tool == 'y' || tool == 'w') {
1564                 BMIter iter, liter;
1565                 BMFace *efa;
1566                 BMLoop *l;
1567
1568                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1569                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1570                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1571                                 continue;
1572
1573                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1574                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1575                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1576                                         luv->uv[1] = cent[1];
1577                                 }
1578
1579                         }
1580                 }
1581         }
1582
1583         if (tool == 's' || tool == 't' || tool == 'u') {
1584                 BMEdge *eed;
1585                 BMLoop *l;
1586                 BMVert *eve;
1587                 BMVert *eve_start;
1588                 BMIter iter, liter, eiter;
1589
1590                 /* clear tag */
1591                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1592                         BM_elem_flag_disable(eve, BM_ELEM_TAG);
1593                 }
1594
1595                 /* tag verts with a selected UV */
1596                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1597                         BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
1598                                 tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1599
1600                                 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1601                                         continue;
1602
1603                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1604                                         BM_elem_flag_enable(eve, BM_ELEM_TAG);
1605                                         break;
1606                                 }
1607                         }
1608                 }
1609
1610                 /* flush vertex tags to edges */
1611                 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1612                         BM_elem_flag_set(eed, BM_ELEM_TAG, (BM_elem_flag_test(eed->v1, BM_ELEM_TAG) &&
1613                                                             BM_elem_flag_test(eed->v2, BM_ELEM_TAG)));
1614                 }
1615
1616                 /* find a vertex with only one tagged edge */
1617                 eve_start = NULL;
1618                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1619                         int tot_eed_tag = 0;
1620                         BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
1621                                 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
1622                                         tot_eed_tag++;
1623                                 }
1624                         }
1625
1626                         if (tot_eed_tag == 1) {
1627                                 eve_start = eve;
1628                                 break;
1629                         }
1630                 }
1631
1632                 if (eve_start) {
1633                         BMVert **eve_line = NULL;
1634                         BMVert *eve_next = NULL;
1635                         BLI_array_declare(eve_line);
1636                         int i;
1637
1638                         eve = eve_start;
1639
1640                         /* walk over edges, building an array of verts in a line */
1641                         while (eve) {
1642                                 BLI_array_append(eve_line, eve);
1643                                 /* don't touch again */
1644                                 BM_elem_flag_disable(eve, BM_ELEM_TAG);
1645
1646                                 eve_next = NULL;
1647
1648                                 /* find next eve */
1649                                 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
1650                                         if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
1651                                                 BMVert *eve_other = BM_edge_other_vert(eed, eve);
1652                                                 if (BM_elem_flag_test(eve_other, BM_ELEM_TAG)) {
1653                                                         /* this is a tagged vert we didnt walk over yet, step onto it */
1654                                                         eve_next = eve_other;
1655                                                         break;
1656                                                 }
1657                                         }
1658                                 }
1659
1660                                 eve = eve_next;
1661                         }
1662
1663                         /* now we have all verts, make into a line */
1664                         if (BLI_array_count(eve_line) > 2) {
1665
1666                                 /* we know the returns from these must be valid */
1667                                 float *uv_start = uv_sel_co_from_eve(scene, ima, em, eve_line[0]);
1668                                 float *uv_end   = uv_sel_co_from_eve(scene, ima, em, eve_line[BLI_array_count(eve_line) - 1]);
1669                                 /* For t & u modes */
1670                                 float a = 0.0f;
1671
1672                                 if (tool == 't') {
1673                                         if (uv_start[1] == uv_end[1])
1674                                                 tool = 's';
1675                                         else
1676                                                 a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
1677                                 }
1678                                 else if (tool == 'u') {
1679                                         if (uv_start[0] == uv_end[0])
1680                                                 tool = 's';
1681                                         else
1682                                                 a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
1683                                 }
1684
1685                                 /* go over all verts except for endpoints */
1686                                 for (i = 0; i < BLI_array_count(eve_line); i++) {
1687                                         BM_ITER_ELEM (l, &liter, eve_line[i], BM_LOOPS_OF_VERT) {
1688                                                 tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1689
1690                                                 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1691                                                         continue;
1692
1693                                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1694                                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1695                                                         /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
1696                                                          * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
1697                                                          * Maybe this should be a BLI func? Or is it already existing?
1698                                                          * Could use interp_v2_v2v2, but not sure it's worth it here...*/
1699                                                         if (tool == 't')
1700                                                                 luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0];
1701                                                         else if (tool == 'u')
1702                                                                 luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1];
1703                                                         else
1704                                                                 closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end);
1705                                                 }
1706                                         }
1707                                 }
1708                         }
1709                         else {
1710                                 /* error - not a line, needs 3+ points  */
1711                         }
1712
1713                         if (eve_line) {
1714                                 MEM_freeN(eve_line);
1715                         }
1716                 }
1717                 else {
1718                         /* error - cant find an endpoint */
1719                 }
1720         }
1721
1722
1723         uvedit_live_unwrap_update(sima, scene, obedit);
1724         DAG_id_tag_update(obedit->data, 0);
1725         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1726 }
1727
1728 static int uv_align_exec(bContext *C, wmOperator *op)
1729 {
1730         uv_weld_align(C, RNA_enum_get(op->ptr, "axis"));
1731
1732         return OPERATOR_FINISHED;
1733 }
1734
1735 static void UV_OT_align(wmOperatorType *ot)
1736 {
1737         static EnumPropertyItem axis_items[] = {
1738                 {'s', "ALIGN_S", 0, "Straighten", "Align UVs along the line defined by the endpoints"},
1739                 {'t', "ALIGN_T", 0, "Straighten X", "Align UVs along the line defined by the endpoints along the X axis"},
1740                 {'u', "ALIGN_U", 0, "Straighten Y", "Align UVs along the line defined by the endpoints along the Y axis"},
1741                 {'a', "ALIGN_AUTO", 0, "Align Auto", "Automatically choose the axis on which there is most alignment already"},
1742                 {'x', "ALIGN_X", 0, "Align X", "Align UVs on X axis"},
1743                 {'y', "ALIGN_Y", 0, "Align Y", "Align UVs on Y axis"},
1744                 {0, NULL, 0, NULL, NULL}};
1745
1746         /* identifiers */
1747         ot->name = "Align";
1748         ot->description = "Align selected UV vertices to an axis";
1749         ot->idname = "UV_OT_align";
1750         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1751         
1752         /* api callbacks */
1753         ot->exec = uv_align_exec;
1754         ot->poll = ED_operator_uvedit;
1755
1756         /* properties */
1757         RNA_def_enum(ot->srna, "axis", axis_items, 'a', "Axis", "Axis to align UV locations on");
1758 }
1759 /* ******************** weld near operator **************** */
1760
1761 typedef struct UVvert {
1762         MLoopUV *uv_loop;
1763         int weld;
1764 } UVvert;
1765
1766 static int uv_remove_doubles_exec(bContext *C, wmOperator *op)
1767 {
1768         const float threshold = RNA_float_get(op->ptr, "threshold");
1769         const int use_unselected = RNA_boolean_get(op->ptr, "use_unselected");
1770
1771         SpaceImage *sima;
1772         Scene *scene;
1773         Object *obedit = CTX_data_edit_object(C);
1774         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1775         Image *ima;
1776         MTexPoly *tf;
1777         int uv_a_index;
1778         int uv_b_index;
1779         float *uv_a;
1780         float *uv_b;
1781
1782         BMIter iter, liter;
1783         BMFace *efa;
1784         BMLoop *l;
1785
1786         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1787         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1788
1789         sima = CTX_wm_space_image(C);
1790         scene = CTX_data_scene(C);
1791         ima = CTX_data_edit_image(C);
1792
1793         if (use_unselected == FALSE) {
1794                 UVvert *vert_arr = NULL;
1795                 BLI_array_declare(vert_arr);
1796                 MLoopUV **loop_arr = NULL;
1797                 BLI_array_declare(loop_arr);
1798
1799                 /* TODO, use qsort as with MESH_OT_remove_doubles, this isn't optimal */
1800                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1801                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1802                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1803                                 continue;
1804
1805                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1806                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1807                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1808                                         UVvert vert;
1809                                         vert.uv_loop = luv;
1810                                         vert.weld = FALSE;
1811                                         BLI_array_append(vert_arr, vert);
1812                                 }
1813
1814                         }
1815                 }
1816
1817                 for (uv_a_index = 0; uv_a_index < BLI_array_count(vert_arr); uv_a_index++) {
1818                         if (vert_arr[uv_a_index].weld == FALSE) {
1819                                 float uv_min[2];
1820                                 float uv_max[2];
1821
1822                                 BLI_array_empty(loop_arr);
1823                                 BLI_array_append(loop_arr, vert_arr[uv_a_index].uv_loop);
1824
1825                                 uv_a = vert_arr[uv_a_index].uv_loop->uv;
1826
1827                                 copy_v2_v2(uv_max, uv_a);
1828                                 copy_v2_v2(uv_min, uv_a);
1829
1830                                 vert_arr[uv_a_index].weld = TRUE;
1831                                 for (uv_b_index = uv_a_index + 1; uv_b_index < BLI_array_count(vert_arr); uv_b_index++) {
1832                                         uv_b = vert_arr[uv_b_index].uv_loop->uv;
1833                                         if ((vert_arr[uv_b_index].weld == FALSE) &&
1834                                             (len_manhattan_v2v2(uv_a, uv_b) < threshold))
1835                                         {
1836                                                 minmax_v2v2_v2(uv_max, uv_min, uv_b);
1837                                                 BLI_array_append(loop_arr, vert_arr[uv_b_index].uv_loop);
1838                                                 vert_arr[uv_b_index].weld = TRUE;
1839                                         }
1840                                 }
1841                                 if (BLI_array_count(loop_arr)) {
1842                                         float uv_mid[2];
1843                                         mid_v2_v2v2(uv_mid, uv_min, uv_max);
1844                                         for (uv_b_index = 0; uv_b_index < BLI_array_count(loop_arr); uv_b_index++) {
1845                                                 copy_v2_v2(loop_arr[uv_b_index]->uv, uv_mid);
1846                                         }
1847                                 }
1848                         }
1849                 }
1850
1851                 BLI_array_free(vert_arr);
1852                 BLI_array_free(loop_arr);
1853         }
1854         else {
1855                 /* selected -> unselected
1856                  *
1857                  * No need to use 'UVvert' here */
1858                 MLoopUV **loop_arr = NULL;
1859                 BLI_array_declare(loop_arr);
1860                 MLoopUV **loop_arr_unselected = NULL;
1861                 BLI_array_declare(loop_arr_unselected);
1862
1863                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1864                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1865                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1866                                 continue;
1867
1868                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1869                                 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1870                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1871                                         BLI_array_append(loop_arr, luv);
1872                                 }
1873                                 else {
1874                                         BLI_array_append(loop_arr_unselected, luv);
1875                                 }
1876                         }
1877                 }
1878
1879                 for (uv_a_index = 0; uv_a_index < BLI_array_count(loop_arr); uv_a_index++) {
1880                         float dist_best = FLT_MAX, dist;
1881                         float *uv_best = NULL;
1882
1883                         uv_a = loop_arr[uv_a_index]->uv;
1884                         for (uv_b_index = 0; uv_b_index < BLI_array_count(loop_arr_unselected); uv_b_index++) {
1885                                 uv_b = loop_arr_unselected[uv_b_index]->uv;
1886                                 dist = len_manhattan_v2v2(uv_a, uv_b);
1887                                 if ((dist < threshold) && (dist < dist_best)) {
1888                                         uv_best = uv_b;
1889                                         dist_best = dist;
1890                                 }
1891                         }
1892                         if (uv_best) {
1893                                 copy_v2_v2(uv_a, uv_best);
1894                         }
1895                 }
1896
1897                 BLI_array_free(loop_arr);
1898                 BLI_array_free(loop_arr_unselected);
1899         }
1900
1901         uvedit_live_unwrap_update(sima, scene, obedit);
1902         DAG_id_tag_update(obedit->data, 0);
1903         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1904
1905         return OPERATOR_FINISHED;
1906 }
1907
1908 static void UV_OT_remove_doubles(wmOperatorType *ot)
1909 {
1910         /* identifiers */
1911         ot->name = "Remove Doubles UV";
1912         ot->description = "Selected UV vertices that are within a radius of each other are welded together";
1913         ot->idname = "UV_OT_remove_doubles";
1914         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1915
1916         /* api callbacks */
1917         ot->exec = uv_remove_doubles_exec;
1918         ot->poll = ED_operator_uvedit;
1919
1920         RNA_def_float(ot->srna, "threshold", 0.02f, 0.0f, 10.0f,
1921                       "Merge Distance", "Maximum distance between welded vertices", 0.0f, 1.0f);
1922         RNA_def_boolean(ot->srna, "use_unselected", 0, "Unselected", "Merge selected to other unselected vertices");
1923 }
1924 /* ******************** weld operator **************** */
1925
1926 static int uv_weld_exec(bContext *C, wmOperator *UNUSED(op))
1927 {
1928         uv_weld_align(C, 'w');
1929
1930         return OPERATOR_FINISHED;
1931 }
1932
1933 static void UV_OT_weld(wmOperatorType *ot)
1934 {
1935         /* identifiers */
1936         ot->name = "Weld";
1937         ot->description = "Weld selected UV vertices together";
1938         ot->idname = "UV_OT_weld";
1939         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1940         
1941         /* api callbacks */
1942         ot->exec = uv_weld_exec;
1943         ot->poll = ED_operator_uvedit;
1944 }
1945
1946
1947 /* ******************** (de)select all operator **************** */
1948
1949 static void uv_select_all_perform(Scene *scene, Image *ima, BMEditMesh *em, int action)
1950 {
1951         ToolSettings *ts = scene->toolsettings;
1952         BMFace *efa;
1953         BMLoop *l;
1954         BMIter iter, liter;
1955         MTexPoly *tf;
1956         MLoopUV *luv;
1957
1958         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1959         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1960
1961         if (ts->uv_flag & UV_SYNC_SELECTION) {
1962
1963                 switch (action) {
1964                         case SEL_TOGGLE:
1965                                 EDBM_select_toggle_all(em);
1966                                 break;
1967                         case SEL_SELECT:
1968                                 EDBM_flag_enable_all(em, BM_ELEM_SELECT);
1969                                 break;
1970                         case SEL_DESELECT:
1971                                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1972                                 break;
1973                         case SEL_INVERT:
1974                                 EDBM_select_swap(em);
1975                                 EDBM_selectmode_flush(em);
1976                                 break;
1977                 }
1978         }
1979         else {
1980                 if (action == SEL_TOGGLE) {
1981                         action = SEL_SELECT;
1982                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1983                                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1984         
1985                                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
1986                                         continue;
1987
1988                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1989                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1990
1991                                         if (luv->flag & MLOOPUV_VERTSEL) {
1992                                                 action = SEL_DESELECT;
1993                                                 break;
1994                                         }
1995                                 }
1996                         }
1997                 }
1998         
1999                 
2000                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2001                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2002
2003                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
2004                                 continue;
2005
2006                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2007                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2008
2009                                 switch (action) {
2010                                         case SEL_SELECT:
2011                                                 luv->flag |= MLOOPUV_VERTSEL;
2012                                                 break;
2013                                         case SEL_DESELECT:
2014                                                 luv->flag &= ~MLOOPUV_VERTSEL;
2015                                                 break;
2016                                         case SEL_INVERT:
2017                                                 luv->flag ^= MLOOPUV_VERTSEL;
2018                                                 break;
2019                                 }
2020                         }
2021                 }
2022         }
2023 }
2024
2025 static int uv_select_all_exec(bContext *C, wmOperator *op)
2026 {
2027         Scene *scene = CTX_data_scene(C);
2028         Object *obedit = CTX_data_edit_object(C);
2029         Image *ima = CTX_data_edit_image(C);
2030         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2031
2032         int action = RNA_enum_get(op->ptr, "action");
2033
2034         uv_select_all_perform(scene, ima, em, action);
2035
2036         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2037
2038         return OPERATOR_FINISHED;
2039 }
2040
2041 static void UV_OT_select_all(wmOperatorType *ot)
2042 {
2043         /* identifiers */
2044         ot->name = "(De)select All";
2045         ot->description = "Change selection of all UV vertices";
2046         ot->idname = "UV_OT_select_all";
2047         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2048         
2049         /* api callbacks */
2050         ot->exec = uv_select_all_exec;
2051         ot->poll = ED_operator_uvedit;
2052
2053         WM_operator_properties_select_all(ot);
2054 }
2055
2056 /* ******************** mouse select operator **************** */
2057
2058 static bool uv_sticky_select(float *limit, int hitv[4], int v, float *hituv[4], float *uv, int sticky, int hitlen)
2059 {
2060         int i;
2061
2062         /* this function test if some vertex needs to selected
2063          * in addition to the existing ones due to sticky select */
2064         if (sticky == SI_STICKY_DISABLE)
2065                 return false;
2066
2067         for (i = 0; i < hitlen; i++) {
2068                 if (hitv[i] == v) {
2069                         if (sticky == SI_STICKY_LOC) {
2070                                 if (fabsf(hituv[i][0] - uv[0]) < limit[0] && fabsf(hituv[i][1] - uv[1]) < limit[1])
2071                                         return true;
2072                         }
2073                         else if (sticky == SI_STICKY_VERTEX)
2074                                 return true;
2075                 }
2076         }
2077
2078         return false;
2079 }
2080
2081 static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loop)
2082 {
2083         SpaceImage *sima = CTX_wm_space_image(C);
2084         Scene *scene = CTX_data_scene(C);
2085         ToolSettings *ts = scene->toolsettings;
2086         Object *obedit = CTX_data_edit_object(C);
2087         Image *ima = CTX_data_edit_image(C);
2088         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2089         BMFace *efa;
2090         BMLoop *l;
2091         BMIter iter, liter;
2092         MTexPoly *tf;
2093         MLoopUV *luv;
2094         NearestHit hit;
2095         int i, selectmode, sticky, sync, *hitv = NULL;
2096         bool select = true;
2097         BLI_array_declare(hitv);
2098         int flush = 0, hitlen = 0; /* 0 == don't flush, 1 == sel, -1 == desel;  only use when selection sync is enabled */
2099         float limit[2], **hituv = NULL;
2100         BLI_array_declare(hituv);
2101         float penalty[2];
2102
2103         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2104         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2105
2106         /* notice 'limit' is the same no matter the zoom level, since this is like
2107          * remove doubles and could annoying if it joined points when zoomed out.
2108          * 'penalty' is in screen pixel space otherwise zooming in on a uv-vert and
2109          * shift-selecting can consider an adjacent point close enough to add to
2110          * the selection rather than de-selecting the closest. */
2111
2112         uvedit_pixel_to_float(sima, limit, 0.05f);
2113         uvedit_pixel_to_float(sima, penalty, 5.0f / (sima ? sima->zoom : 1.0f));
2114
2115         /* retrieve operation mode */
2116         if (ts->uv_flag & UV_SYNC_SELECTION) {
2117                 sync = 1;
2118
2119                 if (ts->selectmode & SCE_SELECT_FACE)
2120                         selectmode = UV_SELECT_FACE;
2121                 else if (ts->selectmode & SCE_SELECT_EDGE)
2122                         selectmode = UV_SELECT_EDGE;
2123                 else
2124                         selectmode = UV_SELECT_VERTEX;
2125
2126                 sticky = SI_STICKY_DISABLE;
2127         }
2128         else {
2129                 sync = 0;
2130                 selectmode = ts->uv_selectmode;
2131                 sticky = (sima) ? sima->sticky : 1;
2132         }
2133
2134         /* find nearest element */
2135         if (loop) {
2136                 /* find edge */
2137                 uv_find_nearest_edge(scene, ima, em, co, &hit);
2138                 if (hit.efa == NULL) {
2139                         BLI_array_free(hitv);
2140                         BLI_array_free(hituv);
2141                         return OPERATOR_CANCELLED;
2142                 }
2143
2144                 hitlen = 0;
2145         }
2146         else if (selectmode == UV_SELECT_VERTEX) {
2147                 /* find vertex */
2148                 uv_find_nearest_vert(scene, ima, em, co, penalty, &hit);
2149                 if (hit.efa == NULL) {
2150                         BLI_array_free(hitv);
2151                         BLI_array_free(hituv);
2152                         return OPERATOR_CANCELLED;
2153                 }
2154
2155                 /* mark 1 vertex as being hit */
2156                 BLI_array_grow_items(hitv, hit.efa->len);
2157                 BLI_array_grow_items(hituv, hit.efa->len);
2158                 for (i = 0; i < hit.efa->len; i++) {
2159                         hitv[i] = 0xFFFFFFFF;
2160                 }
2161
2162                 hitv[hit.lindex] = hit.vert1;
2163                 hituv[hit.lindex] = hit.luv->uv;
2164
2165                 hitlen = hit.efa->len;
2166         }
2167         else if (selectmode == UV_SELECT_EDGE) {
2168                 /* find edge */
2169                 uv_find_nearest_edge(scene, ima, em, co, &hit);
2170                 if (hit.efa == NULL) {
2171                         BLI_array_free(hitv);
2172                         BLI_array_free(hituv);
2173                         return OPERATOR_CANCELLED;
2174                 }
2175
2176                 /* mark 2 edge vertices as being hit */
2177                 BLI_array_grow_items(hitv,  hit.efa->len);
2178                 BLI_array_grow_items(hituv, hit.efa->len);
2179                 fill_vn_i(hitv, hit.efa->len, 0xFFFFFFFF);
2180
2181                 hitv[hit.lindex] = hit.vert1;
2182                 hitv[(hit.lindex + 1) % hit.efa->len] = hit.vert2;
2183                 hituv[hit.lindex] = hit.luv->uv;
2184                 hituv[(hit.lindex + 1) % hit.efa->len] = hit.luv_next->uv;
2185
2186                 hitlen = hit.efa->len;
2187         }
2188         else if (selectmode == UV_SELECT_FACE) {
2189                 /* find face */
2190                 uv_find_nearest_face(scene, ima, em, co, &hit);
2191                 if (hit.efa == NULL) {
2192                         BLI_array_free(hitv);
2193                         BLI_array_free(hituv);
2194                         return OPERATOR_CANCELLED;
2195                 }
2196                 
2197                 /* make active */
2198                 BM_active_face_set(em->bm, hit.efa);
2199
2200                 /* mark all face vertices as being hit */
2201
2202                 BLI_array_grow_items(hitv,  hit.efa->len);
2203                 BLI_array_grow_items(hituv, hit.efa->len);
2204                 i = 0;
2205                 BM_ITER_ELEM (l, &liter, hit.efa, BM_LOOPS_OF_FACE) {
2206                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2207                         hituv[i] = luv->uv;
2208                         hitv[i] = BM_elem_index_get(l->v);
2209                         i++;
2210                 }
2211                 
2212                 hitlen = hit.efa->len;
2213         }
2214         else if (selectmode == UV_SELECT_ISLAND) {
2215                 uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
2216
2217                 if (hit.efa == NULL) {
2218                         BLI_array_free(hitv);
2219                         BLI_array_free(hituv);
2220                         return OPERATOR_CANCELLED;
2221                 }
2222
2223                 hitlen = 0;
2224         }
2225         else {
2226                 hitlen = 0;
2227                 BLI_array_free(hitv);
2228                 BLI_array_free(hituv);
2229                 return OPERATOR_CANCELLED;
2230         }
2231
2232         /* do selection */
2233         if (loop) {
2234                 flush = uv_select_edgeloop(scene, ima, em, &hit, limit, extend);
2235         }
2236         else if (selectmode == UV_SELECT_ISLAND) {
2237                 uv_select_linked(scene, ima, em, limit, &hit, extend);
2238         }
2239         else if (extend) {
2240                 if (selectmode == UV_SELECT_VERTEX) {
2241                         /* (de)select uv vertex */
2242                         select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
2243                         uvedit_uv_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset);
2244                         flush = 1;
2245                 }
2246                 else if (selectmode == UV_SELECT_EDGE) {
2247                         /* (de)select edge */
2248                         select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset));
2249                         uvedit_edge_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset);
2250                         flush = 1;
2251                 }
2252                 else if (selectmode == UV_SELECT_FACE) {
2253                         /* (de)select face */
2254                         select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset));
2255                         uvedit_face_select_set(scene, em, hit.efa, select, true, cd_loop_uv_offset);
2256                         flush = -1;
2257                 }
2258
2259                 /* de-selecting an edge may deselect a face too - validate */
2260                 if (sync) {
2261                         if (select == FALSE) {
2262                                 BM_select_history_validate(em->bm);
2263                         }
2264                 }
2265
2266                 /* (de)select sticky uv nodes */
2267                 if (sticky != SI_STICKY_DISABLE) {
2268
2269                         BM_mesh_elem_index_ensure(em->bm, BM_VERT);
2270
2271                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2272                                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2273                                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2274                                         continue;
2275
2276                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2277                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2278                                         if (uv_sticky_select(limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen))
2279                                                 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2280                                 }
2281                         }
2282
2283                         flush = select ? 1 : -1;
2284                 }
2285         }
2286         else {
2287                 /* deselect all */
2288                 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
2289
2290                 if (selectmode == UV_SELECT_VERTEX) {
2291                         /* select vertex */
2292                         uvedit_uv_select_enable(em, scene, hit.l, true, cd_loop_uv_offset);
2293                         flush = 1;
2294                 }
2295                 else if (selectmode == UV_SELECT_EDGE) {
2296                         /* select edge */
2297                         uvedit_edge_select_enable(em, scene, hit.l, true, cd_loop_uv_offset);
2298                         flush = 1;
2299                 }
2300                 else if (selectmode == UV_SELECT_FACE) {
2301                         /* select face */
2302                         uvedit_face_select_enable(scene, em, hit.efa, true, cd_loop_uv_offset);
2303                 }
2304
2305                 /* select sticky uvs */
2306                 if (sticky != SI_STICKY_DISABLE) {
2307                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2308                                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2309                                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2310                                         continue;
2311                                 
2312                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2313                                         if (sticky == SI_STICKY_DISABLE) continue;
2314                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2315
2316                                         if (uv_sticky_select(limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen))
2317                                                 uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset);
2318
2319                                         flush = 1;
2320                                 }
2321                         }
2322                 }
2323         }
2324
2325         if (sync) {
2326                 /* flush for mesh selection */
2327
2328                 /* before bmesh */
2329 #if 0
2330                 if (ts->selectmode != SCE_SELECT_FACE) {
2331                         if (flush == 1) EDBM_select_flush(em);
2332                         else if (flush == -1) EDBM_deselect_flush(em);
2333                 }
2334 #else
2335                 if (flush != 0) {
2336                         if (loop) {
2337                                 /* push vertex -> edge selection */
2338                                 if (select) {
2339                                         EDBM_select_flush(em);
2340                                 }
2341                                 else {
2342                                         EDBM_deselect_flush(em);
2343                                 }
2344                         }
2345                         else {
2346                                 EDBM_selectmode_flush(em);
2347                         }
2348                 }
2349 #endif
2350         }
2351
2352         DAG_id_tag_update(obedit->data, 0);
2353         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2354
2355         BLI_array_free(hitv);
2356         BLI_array_free(hituv);
2357
2358         return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
2359 }
2360
2361 static int uv_select_exec(bContext *C, wmOperator *op)
2362 {
2363         float co[2];
2364         bool extend, loop;
2365
2366         RNA_float_get_array(op->ptr, "location", co);
2367         extend = RNA_boolean_get(op->ptr, "extend");
2368         loop = false;
2369
2370         return uv_mouse_select(C, co, extend, loop);
2371 }
2372
2373 static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2374 {
2375         ARegion *ar = CTX_wm_region(C);
2376         float co[2];
2377
2378         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2379         RNA_float_set_array(op->ptr, "location", co);
2380
2381         return uv_select_exec(C, op);
2382 }
2383
2384 static void UV_OT_select(wmOperatorType *ot)
2385 {
2386         /* identifiers */
2387         ot->name = "Select";
2388         ot->description = "Select UV vertices";
2389         ot->idname = "UV_OT_select";
2390         ot->flag = OPTYPE_UNDO;
2391         
2392         /* api callbacks */
2393         ot->exec = uv_select_exec;
2394         ot->invoke = uv_select_invoke;
2395         ot->poll = ED_operator_uvedit; /* requires space image */;
2396
2397         /* properties */
2398         RNA_def_boolean(ot->srna, "extend", 0,
2399                         "Extend", "Extend selection rather than clearing the existing selection");
2400         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2401                              "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2402 }
2403
2404 /* ******************** loop select operator **************** */
2405
2406 static int uv_select_loop_exec(bContext *C, wmOperator *op)
2407 {
2408         float co[2];
2409         bool extend, loop;
2410
2411         RNA_float_get_array(op->ptr, "location", co);
2412         extend = RNA_boolean_get(op->ptr, "extend");
2413         loop = true;
2414
2415         return uv_mouse_select(C, co, extend, loop);
2416 }
2417
2418 static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2419 {
2420         ARegion *ar = CTX_wm_region(C);
2421         float co[2];
2422
2423         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2424         RNA_float_set_array(op->ptr, "location", co);
2425
2426         return uv_select_loop_exec(C, op);
2427 }
2428
2429 static void UV_OT_select_loop(wmOperatorType *ot)
2430 {
2431         /* identifiers */
2432         ot->name = "Loop Select";
2433         ot->description = "Select a loop of connected UV vertices";
2434         ot->idname = "UV_OT_select_loop";
2435         ot->flag = OPTYPE_UNDO;
2436         
2437         /* api callbacks */
2438         ot->exec = uv_select_loop_exec;
2439         ot->invoke = uv_select_loop_invoke;
2440         ot->poll = ED_operator_uvedit; /* requires space image */;
2441
2442         /* properties */
2443         RNA_def_boolean(ot->srna, "extend", 0,
2444                         "Extend", "Extend selection rather than clearing the existing selection");
2445         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2446                              "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2447 }
2448
2449 /* ******************** linked select operator **************** */
2450
2451 static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, int pick)
2452 {
2453         SpaceImage *sima = CTX_wm_space_image(C);
2454         Scene *scene = CTX_data_scene(C);
2455         ToolSettings *ts = scene->toolsettings;
2456         Object *obedit = CTX_data_edit_object(C);
2457         Image *ima = CTX_data_edit_image(C);
2458         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2459         float limit[2];
2460         int extend;
2461
2462         NearestHit hit, *hit_p = NULL;
2463
2464         if (ts->uv_flag & UV_SYNC_SELECTION) {
2465                 BKE_report(op->reports, RPT_ERROR, "Cannot select linked when sync selection is enabled");
2466                 return OPERATOR_CANCELLED;
2467         }
2468
2469         extend = RNA_boolean_get(op->ptr, "extend");
2470         uvedit_pixel_to_float(sima, limit, 0.05f);
2471
2472         if (pick) {
2473                 float co[2];
2474
2475                 if (event) {
2476                         /* invoke */
2477                         ARegion *ar = CTX_wm_region(C);
2478
2479                         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2480                         RNA_float_set_array(op->ptr, "location", co);
2481                 }
2482                 else {
2483                         /* exec */
2484                         RNA_float_get_array(op->ptr, "location", co);
2485                 }
2486
2487                 uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
2488                 hit_p = &hit;
2489         }
2490
2491         uv_select_linked(scene, ima, em, limit, hit_p, extend);
2492
2493         DAG_id_tag_update(obedit->data, 0);
2494         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2495
2496         return OPERATOR_FINISHED;
2497 }
2498
2499 static int uv_select_linked_exec(bContext *C, wmOperator *op)
2500 {
2501         return uv_select_linked_internal(C, op, NULL, 0);
2502 }
2503
2504 static void UV_OT_select_linked(wmOperatorType *ot)
2505 {
2506         /* identifiers */
2507         ot->name = "Select Linked";
2508         ot->description = "Select all UV vertices linked to the active UV map";
2509         ot->idname = "UV_OT_select_linked";
2510         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2511         
2512         /* api callbacks */
2513         ot->exec = uv_select_linked_exec;
2514         ot->poll = ED_operator_uvedit;    /* requires space image */
2515
2516         /* properties */
2517         RNA_def_boolean(ot->srna, "extend", 0,
2518                         "Extend", "Extend selection rather than clearing the existing selection");
2519 }
2520
2521 static int uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2522 {
2523         return uv_select_linked_internal(C, op, event, 1);
2524 }
2525
2526 static int uv_select_linked_pick_exec(bContext *C, wmOperator *op)
2527 {
2528         return uv_select_linked_internal(C, op, NULL, 1);
2529 }
2530
2531 static void UV_OT_select_linked_pick(wmOperatorType *ot)
2532 {
2533         /* identifiers */
2534         ot->name = "Select Linked Pick";
2535         ot->description = "Select all UV vertices linked under the mouse";
2536         ot->idname = "UV_OT_select_linked_pick";
2537         ot->flag = OPTYPE_UNDO;
2538
2539         /* api callbacks */
2540         ot->invoke = uv_select_linked_pick_invoke;
2541         ot->exec = uv_select_linked_pick_exec;
2542         ot->poll = ED_operator_uvedit; /* requires space image */;
2543
2544         /* properties */
2545         RNA_def_boolean(ot->srna, "extend", 0,
2546                         "Extend", "Extend selection rather than clearing the existing selection");
2547
2548         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2549                              "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2550 }
2551
2552 /* note: this is based on similar use case to MESH_OT_split(), which has a similar effect
2553  * but in this case they are not joined to begin with (only having the behavior of being joined)
2554  * so its best to call this uv_select_split() instead of just split(), but assigned to the same key
2555  * as MESH_OT_split - Campbell */
2556 static int uv_select_split_exec(bContext *C, wmOperator *op)
2557 {
2558         Scene *scene = CTX_data_scene(C);
2559         ToolSettings *ts = scene->toolsettings;
2560         Image *ima = CTX_data_edit_image(C);
2561         Object *obedit = CTX_data_edit_object(C);
2562         BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2563
2564         BMFace *efa;
2565         BMLoop *l;
2566         BMIter iter, liter;
2567         MTexPoly *tf;
2568         MLoopUV *luv;
2569         bool change = false;
2570
2571         const int cd_loop_uv_offset  = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
2572         const int cd_poly_tex_offset = CustomData_get_offset(&bm->pdata, CD_MTEXPOLY);
2573
2574         if (ts->uv_flag & UV_SYNC_SELECTION) {
2575                 BKE_report(op->reports, RPT_ERROR, "Cannot split selection when sync selection is enabled");
2576                 return OPERATOR_CANCELLED;
2577         }
2578
2579
2580
2581         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2582                 int is_sel = FALSE;
2583                 int is_unsel = FALSE;
2584                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2585
2586                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2587                         continue;
2588
2589                 /* are we all selected? */
2590                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2591                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2592
2593                         if (luv->flag & MLOOPUV_VERTSEL) {
2594                                 is_sel = TRUE;
2595                         }
2596                         else {
2597                                 is_unsel = TRUE;
2598                         }
2599
2600                         /* we have mixed selection, bail out */
2601                         if (is_sel && is_unsel) {
2602                                 break;
2603                         }
2604                 }
2605
2606                 if (is_sel && is_unsel) {
2607                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2608                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2609                                 luv->flag &= ~MLOOPUV_VERTSEL;
2610                         }
2611
2612                         change = true;
2613                 }
2614         }
2615
2616         if (change) {
2617                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
2618                 return OPERATOR_FINISHED;
2619         }
2620         else {
2621                 return OPERATOR_CANCELLED;
2622         }
2623 }
2624
2625
2626 static void UV_OT_select_split(wmOperatorType *ot)
2627 {
2628         /* identifiers */
2629         ot->name = "Select Split";
2630         ot->description = "Select only entirely selected faces";
2631         ot->idname = "UV_OT_select_split";
2632         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2633
2634         /* api callbacks */
2635         ot->exec = uv_select_split_exec;
2636         ot->poll = ED_operator_uvedit; /* requires space image */;
2637 }
2638
2639 /* ******************** unlink selection operator **************** */
2640
2641 static int uv_unlink_selection_exec(bContext *C, wmOperator *op)
2642 {
2643         Scene *scene = CTX_data_scene(C);
2644         ToolSettings *ts = scene->toolsettings;
2645         Object *obedit = CTX_data_edit_object(C);
2646         Image *ima = CTX_data_edit_image(C);
2647         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2648         BMFace *efa;
2649         BMLoop *l;
2650         BMIter iter, liter;
2651         MTexPoly *tf;
2652         MLoopUV *luv;
2653
2654         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2655         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2656
2657         if (ts->uv_flag & UV_SYNC_SELECTION) {
2658                 BKE_report(op->reports, RPT_ERROR, "Cannot unlink selection when sync selection is enabled");
2659                 return OPERATOR_CANCELLED;
2660         }
2661         
2662         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2663                 int desel = 0;
2664
2665                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2666                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2667                         continue;
2668
2669                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2670                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2671                         
2672                         if (!(luv->flag & MLOOPUV_VERTSEL)) {
2673                                 desel = 1;
2674                                 break;
2675                         }
2676                 }
2677
2678                 if (desel) {
2679                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2680                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2681                                 luv->flag &= ~MLOOPUV_VERTSEL;
2682                         }
2683                 }
2684         }
2685         
2686         DAG_id_tag_update(obedit->data, 0);
2687         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2688
2689         return OPERATOR_FINISHED;
2690 }
2691
2692 static void UV_OT_unlink_selected(wmOperatorType *ot)
2693 {
2694         /* identifiers */
2695         ot->name = "Unlink Selection";
2696         ot->description = "Unlink selected UV vertices from active UV map";
2697         ot->idname = "UV_OT_unlink_selected";
2698         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2699         
2700         /* api callbacks */
2701         ot->exec = uv_unlink_selection_exec;
2702         ot->poll = ED_operator_uvedit;
2703 }
2704
2705 static void uv_select_sync_flush(ToolSettings *ts, BMEditMesh *em, const short select)
2706 {
2707         /* bmesh API handles flushing but not on de-select */
2708         if (ts->uv_flag & UV_SYNC_SELECTION) {
2709                 if (ts->selectmode != SCE_SELECT_FACE) {
2710                         if (select == FALSE) {
2711                                 EDBM_deselect_flush(em);
2712                         }
2713                         else {
2714                                 EDBM_select_flush(em);
2715                         }
2716                 }
2717
2718                 if (select == FALSE) {
2719                         BM_select_history_validate(em->bm);
2720                 }
2721         }
2722 }
2723
2724
2725
2726 /* -------------------------------------------------------------------- */
2727 /* Utility functions to flush the uv-selection from tags */
2728
2729 /**
2730  * helper function for #uv_select_flush_from_tag_loop and uv_select_flush_from_tag_face
2731  */
2732 static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene, BMEditMesh *em, UvVertMap *vmap,
2733                                                          const unsigned int efa_index, BMLoop *l,
2734                                                          const bool select, const int cd_loop_uv_offset)
2735 {
2736         UvMapVert *start_vlist = NULL, *vlist_iter;
2737         BMFace *efa_vlist;
2738
2739         uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2740
2741         vlist_iter = EDBM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
2742
2743         while (vlist_iter) {
2744                 if (vlist_iter->separate)
2745                         start_vlist = vlist_iter;
2746
2747                 if (efa_index == vlist_iter->f)
2748                         break;
2749
2750                 vlist_iter = vlist_iter->next;
2751         }
2752
2753         vlist_iter = start_vlist;
2754         while (vlist_iter) {
2755
2756                 if (vlist_iter != start_vlist && vlist_iter->separate)
2757                         break;
2758
2759                 if (efa_index != vlist_iter->f) {
2760                         BMLoop *l_other;
2761                         efa_vlist = EDBM_face_at_index(em, vlist_iter->f);
2762                         /* tf_vlist = BM_ELEM_CD_GET_VOID_P(efa_vlist, cd_poly_tex_offset); */ /* UNUSED */
2763
2764                         l_other = BM_iter_at_index(em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->tfindex);
2765
2766                         uvedit_uv_select_set(em, scene, l_other, select, false, cd_loop_uv_offset);
2767                 }
2768                 vlist_iter = vlist_iter->next;
2769         }
2770 }
2771
2772 /**
2773  * Flush the selection from face tags based on sticky and selection modes.
2774  *
2775  * needed because settings the selection a face is done in a number of places but it also needs to respect
2776  * the sticky modes for the UV verts, so dealing with the sticky modes is best done in a separate function.
2777  *
2778  * \note! This function is very similar to #uv_select_flush_from_tag_loop, be sure to update both upon changing.
2779  */
2780 static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object *obedit, const bool select)
2781 {
2782         /* Selecting UV Faces with some modes requires us to change 
2783          * the selection in other faces (depending on the sticky mode).
2784          * 
2785          * This only needs to be done when the Mesh is not used for
2786          * selection (so for sticky modes, vertex or location based). */
2787         
2788         ToolSettings *ts = scene->toolsettings;
2789         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2790         BMFace *efa;
2791         BMLoop *l;
2792         BMIter iter, liter;
2793         /* MTexPoly *tf; */
2794         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2795         
2796         if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
2797                 /* Tag all verts as untouched, then touch the ones that have a face center
2798                  * in the loop and select all MLoopUV's that use a touched vert. */
2799                 BMVert *eve;
2800                 
2801                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2802                         BM_elem_flag_disable(eve, BM_ELEM_TAG);
2803                 }
2804                 
2805                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2806                         if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2807                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2808                                         BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2809                                 }
2810                         }
2811                 }
2812
2813                 /* now select tagged verts */
2814                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2815                         /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2816
2817                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2818                                 if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
2819                                         uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2820                                 }
2821                         }
2822                 }
2823         }
2824         else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
2825                 struct UvVertMap *vmap;
2826                 float limit[2];
2827                 unsigned int efa_index;
2828                 
2829                 uvedit_pixel_to_float(sima, limit, 0.05);
2830                 
2831                 EDBM_index_arrays_ensure(em, BM_FACE);
2832                 vmap = EDBM_uv_vert_map_create(em, 0, limit);
2833                 if (vmap == NULL) {
2834                         return;
2835                 }
2836                 
2837                 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
2838                         if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2839                                 /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2840                                 
2841                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2842                                         uv_select_flush_from_tag_sticky_loc_internal(scene, em, vmap, efa_index, l,
2843                                                                                      select, cd_loop_uv_offset);
2844                                 }
2845                         }
2846                 }
2847                 EDBM_uv_vert_map_free(vmap);
2848                 
2849         }
2850         else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
2851                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2852                         if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2853                                 uvedit_face_select_set(scene, em, efa, select, false, cd_loop_uv_offset);
2854                         }
2855                 }
2856         }
2857 }
2858
2859
2860
2861 /**
2862  * Flush the selection from loop tags based on sticky and selection modes.
2863  *
2864  * needed because settings the selection a face is done in a number of places but it also needs to respect
2865  * the sticky modes for the UV verts, so dealing with the sticky modes is best done in a separate function.
2866  *
2867  * \note! This function is very similar to #uv_select_flush_from_tag_loop, be sure to update both upon changing.
2868  */
2869 static void uv_select_flush_from_tag_loop(SpaceImage *sima, Scene *scene, Object *obedit, bool select)
2870 {
2871         /* Selecting UV Loops with some modes requires us to change
2872          * the selection in other faces (depending on the sticky mode).
2873          *
2874          * This only needs to be done when the Mesh is not used for
2875          * selection (so for sticky modes, vertex or location based). */
2876
2877         ToolSettings *ts = scene->toolsettings;
2878         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2879         BMFace *efa;
2880         BMLoop *l;
2881         BMIter iter, liter;
2882         /* MTexPoly *tf; */
2883
2884         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2885
2886
2887         if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
2888                 /* Tag all verts as untouched, then touch the ones that have a face center
2889                  * in the loop and select all MLoopUV's that use a touched vert. */
2890                 BMVert *eve;
2891
2892                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2893                         BM_elem_flag_disable(eve, BM_ELEM_TAG);
2894                 }
2895
2896                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2897                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2898                                 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2899                                         BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2900                                 }
2901                         }
2902                 }
2903
2904                 /* now select tagged verts */
2905                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2906                         /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2907
2908                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2909                                 if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
2910                                         uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2911                                 }
2912                         }
2913                 }
2914         }
2915         else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
2916                 struct UvVertMap *vmap;
2917                 float limit[2];
2918                 unsigned int efa_index;
2919
2920                 uvedit_pixel_to_float(sima, limit, 0.05);
2921
2922                 EDBM_index_arrays_ensure(em, BM_FACE);
2923                 vmap = EDBM_uv_vert_map_create(em, 0, limit);
2924                 if (vmap == NULL) {
2925                         return;
2926                 }
2927
2928                 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
2929                         /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2930
2931                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2932                                 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2933                                         uv_select_flush_from_tag_sticky_loc_internal(scene, em, vmap, efa_index, l,
2934                                                                                      select, cd_loop_uv_offset);
2935                                 }
2936                         }
2937                 }
2938                 EDBM_uv_vert_map_free(vmap);
2939
2940         }
2941         else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
2942                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2943                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2944                                 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2945                                         uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2946                                 }
2947                         }
2948                 }
2949         }
2950 }
2951
2952 /* ******************** border select operator **************** */
2953
2954 static int uv_border_select_exec(bContext *C, wmOperator *op)
2955 {
2956         SpaceImage *sima = CTX_wm_space_image(C);
2957         Scene *scene = CTX_data_scene(C);
2958         ToolSettings *ts = scene->toolsettings;
2959         Object *obedit = CTX_data_edit_object(C);
2960         Image *ima = CTX_data_edit_image(C);
2961         ARegion *ar = CTX_wm_region(C);
2962         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2963         BMFace *efa;
2964         BMLoop *l;
2965         BMIter iter, liter;
2966         MTexPoly *tf;
2967         MLoopUV *luv;
2968         rcti rect;
2969         rctf rectf;
2970         bool change, pinned, select, extend;
2971         const bool use_face_center = (ts->uv_flag & UV_SYNC_SELECTION) ?
2972                                     (ts->selectmode == SCE_SELECT_FACE) :
2973                                     (ts->uv_selectmode == UV_SELECT_FACE);
2974
2975         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2976         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2977
2978         /* get rectangle from operator */
2979         WM_operator_properties_border_to_rcti(op, &rect);
2980                 
2981         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
2982         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
2983
2984         /* figure out what to select/deselect */
2985         select = (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT);
2986         pinned = RNA_boolean_get(op->ptr, "pinned");
2987         extend = RNA_boolean_get(op->ptr, "extend");
2988
2989         if (!extend)
2990                 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
2991
2992         /* do actual selection */
2993         if (use_face_center && !pinned) {
2994                 /* handle face selection mode */
2995                 float cent[2];
2996
2997                 change = false;
2998
2999                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3000                         /* assume not touched */
3001                         BM_elem_flag_disable(efa, BM_ELEM_TAG);
3002
3003                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
3004                         if (uvedit_face_visible_test(scene, ima, efa, tf)) {
3005                                 uv_poly_center(efa, cent, cd_loop_uv_offset);
3006                                 if (BLI_rctf_isect_pt_v(&rectf, cent)) {
3007                                         BM_elem_flag_enable(efa, BM_ELEM_TAG);
3008                                         change = true;
3009                                 }
3010                         }
3011                 }
3012
3013                 /* (de)selects all tagged faces and deals with sticky modes */
3014                 if (change) {
3015                         uv_select_flush_from_tag_face(sima, scene, obedit, select);
3016                 }
3017         }
3018         else {
3019                 /* other selection modes */
3020                 change = true;
3021                 
3022                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3023                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
3024                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
3025                                 continue;
3026                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3027                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3028
3029                                 if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION) ) {
3030
3031                                         /* UV_SYNC_SELECTION - can't do pinned selection */
3032                                         if (BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
3033                                                 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
3034                                         }
3035                                 }
3036                                 else if (pinned) {
3037                                         if ((luv->flag & MLOOPUV_PINNED) && BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
3038                                                 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
3039                                         }
3040                                 }
3041                         }
3042                 }
3043         }
3044
3045         if (change) {
3046                 uv_select_sync_flush(ts, em, select);
3047
3048