b0e0dabd6a323eff2b273ff57c1c96897c28a63d
[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 int 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 0;
286
287         if ((ima->tpageflag & IMA_TILES) == 0)
288                 return 0;
289
290         /* skip assigning these procedural images... */
291         if (ima->type == IMA_TYPE_R_RESULT || ima->type == IMA_TYPE_COMPOSITE)
292                 return 0;
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 1;
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 int 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         int sel;
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         sel = 0;
650         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
651                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
652                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
653                         continue;
654                 
655                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
656                         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
657                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
658                                 minmax_v2v2_v2(r_min, r_max, luv->uv);
659                                 sel = 1;
660                         }
661                 }
662         }
663
664         return sel;
665 }
666
667 static int ED_uvedit_median(Scene *scene, Image *ima, Object *obedit, float co[2])
668 {
669         BMEditMesh *em = BKE_editmesh_from_object(obedit);
670         BMFace *efa;
671         BMLoop *l;
672         BMIter iter, liter;
673         MTexPoly *tf;
674         MLoopUV *luv;
675         unsigned int sel = 0;
676
677         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
678         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
679
680         zero_v2(co);
681         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
682                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
683                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
684                         continue;
685                 
686                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
687                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
688                         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
689                                 add_v2_v2(co, luv->uv);
690                                 sel++;
691                         }
692                 }
693         }
694
695         mul_v2_fl(co, 1.0f / (float)sel);
696
697         return (sel != 0);
698 }
699
700 static int uvedit_center(Scene *scene, Image *ima, Object *obedit, float cent[2], char mode)
701 {
702         int change = FALSE;
703         
704         if (mode == V3D_CENTER) {  /* bounding box */
705                 float min[2], max[2];
706                 if (ED_uvedit_minmax(scene, ima, obedit, min, max)) {
707                         mid_v2_v2v2(cent, min, max);
708                         change = TRUE;
709                 }
710         }
711         else {
712                 if (ED_uvedit_median(scene, ima, obedit, cent)) {
713                         change = TRUE;
714                 }
715         }
716
717         return change;
718 }
719
720 /************************** find nearest ****************************/
721
722 void uv_find_nearest_edge(Scene *scene, Image *ima, BMEditMesh *em, const float co[2], NearestHit *hit)
723 {
724         MTexPoly *tf;
725         BMFace *efa;
726         BMLoop *l;
727         BMIter iter, liter;
728         MLoopUV *luv, *luv_next;
729         float mindist_squared, dist_squared;
730         int i;
731
732         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
733         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
734
735         mindist_squared = 1e10f;
736         memset(hit, 0, sizeof(*hit));
737
738         BM_mesh_elem_index_ensure(em->bm, BM_VERT);
739         
740         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
741                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
742                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
743                         continue;
744                 
745                 i = 0;
746                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
747                         luv      = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
748                         luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
749
750                         dist_squared = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
751
752                         if (dist_squared < mindist_squared) {
753                                 hit->tf = tf;
754                                 hit->efa = efa;
755                                 
756                                 hit->l = l;
757                                 hit->nextl = l->next;
758                                 hit->luv = luv;
759                                 hit->luv_next = luv_next;
760                                 hit->lindex = i;
761                                 hit->vert1 = BM_elem_index_get(hit->l->v);
762                                 hit->vert2 = BM_elem_index_get(hit->l->next->v);
763
764                                 mindist_squared = dist_squared;
765                         }
766
767                         i++;
768                 }
769         }
770 }
771
772 static void uv_find_nearest_face(Scene *scene, Image *ima, BMEditMesh *em, const float co[2], NearestHit *hit)
773 {
774         MTexPoly *tf;
775         BMFace *efa;
776         BMIter iter;
777         float mindist, dist, cent[2];
778
779         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
780         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
781
782         mindist = 1e10f;
783         memset(hit, 0, sizeof(*hit));
784
785         /*this will fill in hit.vert1 and hit.vert2*/
786         uv_find_nearest_edge(scene, ima, em, co, hit);
787         hit->l = hit->nextl = NULL;
788         hit->luv = hit->luv_next = NULL;
789
790         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
791                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
792                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
793                         continue;
794
795                 uv_poly_center(efa, cent, cd_loop_uv_offset);
796
797                 dist = fabsf(co[0] - cent[0]) + fabsf(co[1] - cent[1]);
798
799                 if (dist < mindist) {
800                         hit->tf = tf;
801                         hit->efa = efa;
802                         mindist = dist;
803                 }
804         }
805 }
806
807 static int uv_nearest_between(BMEditMesh *em, BMFace *efa, int UNUSED(nverts), int id,
808                               const float co[2], const float uv[2])
809 {
810         BMLoop *l;
811         MLoopUV *luv;
812         BMIter iter;
813         float m[2], v1[2], v2[2], c1, c2, *uv1 = NULL, /* *uv2, */ /* UNUSED */ *uv3 = NULL;
814         int id1, id2, i;
815         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
816
817         id1 = (id + efa->len - 1) % efa->len;
818         id2 = (id + efa->len + 1) % efa->len;
819
820         sub_v2_v2v2(m, co, uv);
821
822         i = 0;
823         BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
824                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
825                 
826                 if (i == id1) {
827                         uv1 = luv->uv;
828                 }
829                 else if (i == id) {
830                         /* uv2 = luv->uv; */ /* UNUSED */
831                 }
832                 else if (i == id2) {
833                         uv3 = luv->uv;
834                 }
835                 i++;
836         }
837
838         sub_v2_v2v2(v1, uv1, uv);
839         sub_v2_v2v2(v2, uv3, uv);
840
841         /* m and v2 on same side of v-v1? */
842         c1 = v1[0] * m[1] - v1[1] * m[0];
843         c2 = v1[0] * v2[1] - v1[1] * v2[0];
844
845         if (c1 * c2 < 0.0f)
846                 return 0;
847
848         /* m and v1 on same side of v-v2? */
849         c1 = v2[0] * m[1] - v2[1] * m[0];
850         c2 = v2[0] * v1[1] - v2[1] * v1[0];
851
852         return (c1 * c2 >= 0.0f);
853 }
854
855 void uv_find_nearest_vert(Scene *scene, Image *ima, BMEditMesh *em,
856                           float const co[2], const float penalty[2], NearestHit *hit)
857 {
858         BMFace *efa;
859         BMLoop *l;
860         BMIter iter, liter;
861         MTexPoly *tf;
862         MLoopUV *luv;
863         float mindist, dist;
864         int i;
865
866         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
867         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
868
869         /*this will fill in hit.vert1 and hit.vert2*/
870         uv_find_nearest_edge(scene, ima, em, co, hit);
871         hit->l = hit->nextl = NULL;
872         hit->luv = hit->luv_next = NULL;
873
874         mindist = 1e10f;
875         memset(hit, 0, sizeof(*hit));
876         
877         BM_mesh_elem_index_ensure(em->bm, BM_VERT);
878
879         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
880                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
881                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
882                         continue;
883                 
884                 i = 0;
885                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
886                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
887
888                         if (penalty && uvedit_uv_select_test(scene, l, cd_loop_uv_offset))
889                                 dist = fabsf(co[0] - luv->uv[0]) + penalty[0] + fabsf(co[1] - luv->uv[1]) + penalty[1];
890                         else
891                                 dist = fabsf(co[0] - luv->uv[0]) + fabsf(co[1] - luv->uv[1]);
892
893                         if (dist <= mindist) {
894                                 if (dist == mindist)
895                                         if (!uv_nearest_between(em, efa, efa->len, i, co, luv->uv)) {
896                                                 i++;
897                                                 continue;
898                                         }
899
900                                 mindist = dist;
901
902                                 hit->l = l;
903                                 hit->nextl = l->next;
904                                 hit->luv = luv;
905                                 hit->luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
906                                 hit->tf = tf;
907                                 hit->efa = efa;
908                                 hit->lindex = i;
909                                 hit->vert1 = BM_elem_index_get(hit->l->v);
910                         }
911
912                         i++;
913                 }
914         }
915 }
916
917 int ED_uvedit_nearest_uv(Scene *scene, Object *obedit, Image *ima, const float co[2], float r_uv[2])
918 {
919         BMEditMesh *em = BKE_editmesh_from_object(obedit);
920         BMFace *efa;
921         BMLoop *l;
922         BMIter iter, liter;
923         MTexPoly *tf;
924         MLoopUV *luv;
925         float mindist, dist;
926         int found = FALSE;
927
928         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
929         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
930
931         mindist = 1e10f;
932         copy_v2_v2(r_uv, co);
933         
934         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
935                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
936                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
937                         continue;
938                 
939                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
940                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
941                         dist = fabsf(co[0] - luv->uv[0]) + fabsf(co[1] - luv->uv[1]);
942
943                         if (dist <= mindist) {
944                                 mindist = dist;
945
946                                 copy_v2_v2(r_uv, luv->uv);
947                                 found = TRUE;
948                         }
949                 }
950         }
951
952         return found;
953 }
954
955 /*********************** loop select ***********************/
956
957 static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first)
958 {
959         UvMapVert *iterv;
960         int count = 0;
961
962         for (iterv = first; iterv; iterv = iterv->next) {
963                 if (iterv->separate && iterv != first)
964                         break;
965
966                 count++;
967         }
968         
969         if (count < 5)
970                 first->flag = 1;
971 }
972
973 static UvMapVert *uv_select_edgeloop_vertex_map_get(UvVertMap *vmap, BMFace *efa, int a)
974 {
975         UvMapVert *iterv, *first;
976         BMLoop *l;
977
978         l = BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, a);
979         first = EDBM_uv_vert_map_at_index(vmap,  BM_elem_index_get(l->v));
980
981         for (iterv = first; iterv; iterv = iterv->next) {
982                 if (iterv->separate)
983                         first = iterv;
984                 if (iterv->f == BM_elem_index_get(efa))
985                         return first;
986         }
987         
988         return NULL;
989 }
990
991 static int uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, UvMapVert *first1, UvMapVert *first2, int *totface)
992 {
993         UvMapVert *iterv1, *iterv2;
994         BMFace *efa;
995         int tot = 0;
996
997         /* count number of faces this edge has */
998         for (iterv1 = first1; iterv1; iterv1 = iterv1->next) {
999                 if (iterv1->separate && iterv1 != first1)
1000                         break;
1001
1002                 for (iterv2 = first2; iterv2; iterv2 = iterv2->next) {
1003                         if (iterv2->separate && iterv2 != first2)
1004                                 break;
1005
1006                         if (iterv1->f == iterv2->f) {
1007                                 /* if face already tagged, don't do this edge */
1008                                 efa = EDBM_face_at_index(em, iterv1->f);
1009                                 if (BM_elem_flag_test(efa, BM_ELEM_TAG))
1010                                         return 0;
1011
1012                                 tot++;
1013                                 break;
1014                         }
1015                 }
1016         }
1017
1018         if (*totface == 0) /* start edge */
1019                 *totface = tot;
1020         else if (tot != *totface) /* check for same number of faces as start edge */
1021                 return 0;
1022
1023         /* tag the faces */
1024         for (iterv1 = first1; iterv1; iterv1 = iterv1->next) {
1025                 if (iterv1->separate && iterv1 != first1)
1026                         break;
1027
1028                 for (iterv2 = first2; iterv2; iterv2 = iterv2->next) {
1029                         if (iterv2->separate && iterv2 != first2)
1030                                 break;
1031
1032                         if (iterv1->f == iterv2->f) {
1033                                 efa = EDBM_face_at_index(em, iterv1->f);
1034                                 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1035                                 break;
1036                         }
1037                 }
1038         }
1039
1040         return 1;
1041 }
1042
1043 static int uv_select_edgeloop(Scene *scene, Image *ima, BMEditMesh *em, NearestHit *hit,
1044                               float limit[2], const bool extend)
1045 {
1046         BMFace *efa;
1047         BMIter iter, liter;
1048         BMLoop *l;
1049         MTexPoly *tf;
1050         UvVertMap *vmap;
1051         UvMapVert *iterv1, *iterv2;
1052         int a, looking, nverts, starttotf, select;
1053
1054         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1055         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1056
1057         /* setup */
1058         EDBM_index_arrays_ensure(em, BM_FACE);
1059         vmap = EDBM_uv_vert_map_create(em, 0, limit);
1060
1061         BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE);
1062
1063         if (!extend) {
1064                 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
1065         }
1066
1067         BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, FALSE);
1068
1069         /* set flags for first face and verts */
1070         nverts = hit->efa->len;
1071         iterv1 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->lindex);
1072         iterv2 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, (hit->lindex + 1) % nverts);
1073         uv_select_edgeloop_vertex_loop_flag(iterv1);
1074         uv_select_edgeloop_vertex_loop_flag(iterv2);
1075
1076         starttotf = 0;
1077         uv_select_edgeloop_edge_tag_faces(em, iterv1, iterv2, &starttotf);
1078
1079         /* sorry, first edge isn't even ok */
1080         if (iterv1->flag == 0 && iterv2->flag == 0) looking = 0;
1081         else looking = 1;
1082
1083         /* iterate */
1084         while (looking) {
1085                 looking = 0;
1086
1087                 /* find correct valence edges which are not tagged yet, but connect to tagged one */
1088
1089                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1090                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1091
1092                         if (!BM_elem_flag_test(efa, BM_ELEM_TAG) && uvedit_face_visible_test(scene, ima, efa, tf)) {
1093                                 nverts = efa->len;
1094                                 for (a = 0; a < nverts; a++) {
1095                                         /* check face not hidden and not tagged */
1096                                         iterv1 = uv_select_edgeloop_vertex_map_get(vmap, efa, a);
1097                                         iterv2 = uv_select_edgeloop_vertex_map_get(vmap, efa, (a + 1) % nverts);
1098                                         
1099                                         if (!iterv1 || !iterv2)
1100                                                 continue;
1101
1102                                         /* check if vertex is tagged and has right valence */
1103                                         if (iterv1->flag || iterv2->flag) {
1104                                                 if (uv_select_edgeloop_edge_tag_faces(em, iterv1, iterv2, &starttotf)) {
1105                                                         looking = 1;
1106                                                         BM_elem_flag_enable(efa, BM_ELEM_TAG);
1107
1108                                                         uv_select_edgeloop_vertex_loop_flag(iterv1);
1109                                                         uv_select_edgeloop_vertex_loop_flag(iterv2);
1110                                                         break;
1111                                                 }
1112                                         }
1113                                 }
1114                         }
1115                 }
1116         }
1117
1118         /* do the actual select/deselect */
1119         nverts = hit->efa->len;
1120         iterv1 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->lindex);
1121         iterv2 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, (hit->lindex + 1) % nverts);
1122         iterv1->flag = 1;
1123         iterv2->flag = 1;
1124
1125         if (extend) {
1126                 if (uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset))
1127                         select = 0;
1128                 else
1129                         select = 1;
1130         }
1131         else
1132                 select = 1;
1133         
1134         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1135                 a = 0;
1136                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1137                         iterv1 = uv_select_edgeloop_vertex_map_get(vmap, efa, a);
1138
1139                         if (iterv1->flag) {
1140                                 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
1141                         }
1142
1143                         a++;
1144                 }
1145         }
1146
1147         /* cleanup */
1148         EDBM_uv_vert_map_free(vmap);
1149
1150         return (select) ? 1 : -1;
1151 }
1152
1153 /*********************** linked select ***********************/
1154
1155 static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const float limit[2], NearestHit *hit, bool extend)
1156 {
1157         BMFace *efa;
1158         BMLoop *l;
1159         BMIter iter, liter;
1160         MTexPoly *tf;
1161         MLoopUV *luv;
1162         UvVertMap *vmap;
1163         UvMapVert *vlist, *iterv, *startv;
1164         int i, stacksize = 0, *stack;
1165         unsigned int a;
1166         char *flag;
1167
1168         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1169         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1170
1171         EDBM_index_arrays_ensure(em, BM_FACE); /* we can use this too */
1172         vmap = EDBM_uv_vert_map_create(em, 1, limit);
1173
1174         if (vmap == NULL)
1175                 return;
1176
1177         stack = MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack");
1178         flag = MEM_callocN(sizeof(*flag) * em->bm->totface, "UvLinkFlag");
1179
1180         if (!hit) {
1181                 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1182                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1183
1184                         if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1185                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1186                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1187
1188                                         if (luv->flag & MLOOPUV_VERTSEL) {
1189                                                 stack[stacksize] = a;
1190                                                 stacksize++;
1191                                                 flag[a] = 1;
1192
1193                                                 break;
1194                                         }
1195                                 }
1196                         }
1197                 }
1198         }
1199         else {
1200                 a = 0;
1201                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1202                         if (efa == hit->efa) {
1203                                 stack[stacksize] = a;
1204                                 stacksize++;
1205                                 flag[a] = 1;
1206                                 break;
1207                         }
1208
1209                         a++;
1210                 }
1211         }
1212
1213         while (stacksize > 0) {
1214                 int j;
1215
1216                 stacksize--;
1217                 a = stack[stacksize];
1218                 
1219                 j = 0;
1220                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1221                         if (j == a)
1222                                 break;
1223
1224                         j++;
1225                 }
1226
1227                 i = 0;
1228                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1229
1230                         /* make_uv_vert_map_EM sets verts tmp.l to the indices */
1231                         vlist = EDBM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
1232                         
1233                         startv = vlist;
1234
1235                         for (iterv = vlist; iterv; iterv = iterv->next) {
1236                                 if (iterv->separate)
1237                                         startv = iterv;
1238                                 if (iterv->f == a)
1239                                         break;
1240                         }
1241
1242                         for (iterv = startv; iterv; iterv = iterv->next) {
1243                                 if ((startv != iterv) && (iterv->separate))
1244                                         break;
1245                                 else if (!flag[iterv->f]) {
1246                                         flag[iterv->f] = 1;
1247                                         stack[stacksize] = iterv->f;
1248                                         stacksize++;
1249                                 }
1250                         }
1251
1252                         i++;
1253                 }
1254         }
1255
1256         if (!extend) {
1257                 a = 0;
1258                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1259                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1260                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1261                                 
1262                                 if (flag[a])
1263                                         luv->flag |= MLOOPUV_VERTSEL;
1264                                 else
1265                                         luv->flag &= ~MLOOPUV_VERTSEL;
1266                         }
1267                         a++;
1268                 }
1269         }
1270         else {
1271                 a = 0;
1272                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1273                         if (!flag[a]) {
1274                                 a++;
1275                                 continue;
1276                         }
1277                         
1278                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1279                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1280                                                 
1281                                 if (luv->flag & MLOOPUV_VERTSEL)
1282                                         break;
1283                         }
1284                         
1285                         if (l)
1286                                 break;
1287                         
1288                         a++;
1289                 }
1290
1291                 if (efa) {
1292                         a = 0;
1293                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1294                                 if (!flag[a]) {
1295                                         a++;
1296                                         continue;
1297                                 }
1298
1299                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1300                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1301                                         
1302                                         luv->flag &= ~MLOOPUV_VERTSEL;
1303                                 }
1304
1305                                 a++;
1306                         }
1307                 }
1308                 else {
1309                         a = 0;
1310                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1311                                 if (!flag[a]) {
1312                                         a++;
1313                                         continue;
1314                                 }
1315
1316                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1317                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1318                                         
1319                                         luv->flag |= MLOOPUV_VERTSEL;
1320                                 }
1321
1322                                 a++;
1323                         }
1324                 }
1325         }
1326         
1327         MEM_freeN(stack);
1328         MEM_freeN(flag);
1329         EDBM_uv_vert_map_free(vmap);
1330 }
1331
1332 /* WATCH IT: this returns first selected UV,
1333  * not ideal in many cases since there could be multiple */
1334 static float *uv_sel_co_from_eve(Scene *scene, Image *ima, BMEditMesh *em, BMVert *eve)
1335 {
1336         BMIter liter;
1337         BMLoop *l;
1338
1339         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1340         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1341
1342         BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
1343                 MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1344
1345                 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1346                         continue;
1347
1348                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1349                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1350                         return luv->uv;
1351                 }
1352         }
1353
1354         return NULL;
1355 }
1356
1357 static int uv_select_more_less(bContext *C, const bool select)
1358 {
1359         Scene *scene = CTX_data_scene(C);
1360         Object *obedit = CTX_data_edit_object(C);
1361         Image *ima = CTX_data_edit_image(C);
1362         SpaceImage *sima = CTX_wm_space_image(C);
1363         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1364
1365         BMFace *efa;
1366         BMLoop *l;
1367         BMIter iter, liter;
1368         ToolSettings *ts = scene->toolsettings;
1369
1370         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1371         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1372
1373         if (ts->uv_flag & UV_SYNC_SELECTION) {
1374                 if (select) {
1375                         EDBM_select_more(em);
1376                 }
1377                 else {
1378                         EDBM_select_less(em);
1379                 }
1380
1381                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1382                 return OPERATOR_FINISHED;
1383         }
1384
1385         if (ts->uv_selectmode == UV_SELECT_FACE) {
1386
1387                 /* clear tags */
1388                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1389                         BM_elem_flag_disable(efa, BM_ELEM_TAG);
1390                 }
1391
1392                 /* mark loops to be selected */
1393                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1394                         MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1395
1396                         if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1397
1398 #define IS_SEL   1
1399 #define IS_UNSEL 2
1400
1401                                 int sel_state = 0;
1402
1403                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1404                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1405                                         if (luv->flag & MLOOPUV_VERTSEL) {
1406                                                 sel_state |= IS_SEL;
1407                                         }
1408                                         else {
1409                                                 sel_state |= IS_UNSEL;
1410                                         }
1411
1412                                         /* if we have a mixed selection, tag to grow it */
1413                                         if (sel_state == (IS_SEL | IS_UNSEL)) {
1414                                                 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1415                                                 break;
1416                                         }
1417                                 }
1418
1419 #undef IS_SEL
1420 #undef IS_UNSEL
1421
1422                         }
1423                 }
1424
1425                 /* select tagged faces */
1426                 uv_select_flush_from_tag_face(sima, scene, obedit, select);
1427         }
1428         else {
1429
1430                 /* clear tags */
1431                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1432                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1433                                 BM_elem_flag_disable(l, BM_ELEM_TAG);
1434                         }
1435                 }
1436
1437                 /* mark loops to be selected */
1438                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1439                         MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1440
1441                         if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1442                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1443
1444                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1445
1446                                         if (((luv->flag & MLOOPUV_VERTSEL) != 0) == select) {
1447                                                 BM_elem_flag_enable(l->next, BM_ELEM_TAG);
1448                                                 BM_elem_flag_enable(l->prev, BM_ELEM_TAG);
1449                                         }
1450                                 }
1451                         }
1452                 }
1453
1454                 /* select tagged loops */
1455                 uv_select_flush_from_tag_loop(sima, scene, obedit, select);
1456         }
1457
1458         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1459
1460         return OPERATOR_FINISHED;
1461 }
1462
1463 static int uv_select_more_exec(bContext *C, wmOperator *UNUSED(op))
1464 {
1465         return uv_select_more_less(C, true);
1466 }
1467
1468 static void UV_OT_select_more(wmOperatorType *ot)
1469 {
1470         /* identifiers */
1471         ot->name = "Select More";
1472         ot->description = "Select more UV vertices connected to initial selection";
1473         ot->idname = "UV_OT_select_more";
1474         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1475
1476         /* api callbacks */
1477         ot->exec = uv_select_more_exec;
1478         ot->poll = ED_operator_uvedit_space_image;
1479 }
1480
1481 static int uv_select_less_exec(bContext *C, wmOperator *UNUSED(op))
1482 {
1483         return uv_select_more_less(C, false);
1484 }
1485
1486 static void UV_OT_select_less(wmOperatorType *ot)
1487 {
1488         /* identifiers */
1489         ot->name = "Select Less";
1490         ot->description = "Deselect UV vertices at the boundary of each selection region";
1491         ot->idname = "UV_OT_select_less";
1492         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1493
1494         /* api callbacks */
1495         ot->exec = uv_select_less_exec;
1496         ot->poll = ED_operator_uvedit_space_image;
1497 }
1498
1499 /* ******************** align operator **************** */
1500
1501 static void uv_weld_align(bContext *C, int tool)
1502 {
1503         Object *obedit = CTX_data_edit_object(C);
1504         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1505         SpaceImage *sima;
1506         Scene *scene;
1507         Image *ima;
1508         MTexPoly *tf;
1509         float cent[2], min[2], max[2];
1510
1511         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1512         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1513
1514         scene = CTX_data_scene(C);
1515         ima = CTX_data_edit_image(C);
1516         sima = CTX_wm_space_image(C);
1517
1518         INIT_MINMAX2(min, max);
1519
1520         if (tool == 'a') {
1521                 BMIter iter, liter;
1522                 BMFace *efa;
1523                 BMLoop *l;
1524
1525                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1526                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1527
1528                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1529                                 continue;
1530
1531                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1532                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1533                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1534                                         minmax_v2v2_v2(min, max, luv->uv);
1535                                 }
1536                         }
1537                 }
1538
1539                 tool = (max[0] - min[0] >= max[1] - min[1]) ? 'y' : 'x';
1540         }
1541
1542         uvedit_center(scene, ima, obedit, cent, 0);
1543
1544         if (tool == 'x' || tool == 'w') {
1545                 BMIter iter, liter;
1546                 BMFace *efa;
1547                 BMLoop *l;
1548
1549                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1550                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1551                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1552                                 continue;
1553
1554                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1555                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1556                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1557                                         luv->uv[0] = cent[0];
1558                                 }
1559
1560                         }
1561                 }
1562         }
1563
1564         if (tool == 'y' || tool == 'w') {
1565                 BMIter iter, liter;
1566                 BMFace *efa;
1567                 BMLoop *l;
1568
1569                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1570                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1571                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1572                                 continue;
1573
1574                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1575                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1576                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1577                                         luv->uv[1] = cent[1];
1578                                 }
1579
1580                         }
1581                 }
1582         }
1583
1584         if (tool == 's' || tool == 't' || tool == 'u') {
1585                 BMEdge *eed;
1586                 BMLoop *l;
1587                 BMVert *eve;
1588                 BMVert *eve_start;
1589                 BMIter iter, liter, eiter;
1590
1591                 /* clear tag */
1592                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1593                         BM_elem_flag_disable(eve, BM_ELEM_TAG);
1594                 }
1595
1596                 /* tag verts with a selected UV */
1597                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1598                         BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
1599                                 tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1600
1601                                 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1602                                         continue;
1603
1604                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1605                                         BM_elem_flag_enable(eve, BM_ELEM_TAG);
1606                                         break;
1607                                 }
1608                         }
1609                 }
1610
1611                 /* flush vertex tags to edges */
1612                 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1613                         BM_elem_flag_set(eed, BM_ELEM_TAG, (BM_elem_flag_test(eed->v1, BM_ELEM_TAG) &&
1614                                                             BM_elem_flag_test(eed->v2, BM_ELEM_TAG)));
1615                 }
1616
1617                 /* find a vertex with only one tagged edge */
1618                 eve_start = NULL;
1619                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1620                         int tot_eed_tag = 0;
1621                         BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
1622                                 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
1623                                         tot_eed_tag++;
1624                                 }
1625                         }
1626
1627                         if (tot_eed_tag == 1) {
1628                                 eve_start = eve;
1629                                 break;
1630                         }
1631                 }
1632
1633                 if (eve_start) {
1634                         BMVert **eve_line = NULL;
1635                         BMVert *eve_next = NULL;
1636                         BLI_array_declare(eve_line);
1637                         int i;
1638
1639                         eve = eve_start;
1640
1641                         /* walk over edges, building an array of verts in a line */
1642                         while (eve) {
1643                                 BLI_array_append(eve_line, eve);
1644                                 /* don't touch again */
1645                                 BM_elem_flag_disable(eve, BM_ELEM_TAG);
1646
1647                                 eve_next = NULL;
1648
1649                                 /* find next eve */
1650                                 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
1651                                         if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
1652                                                 BMVert *eve_other = BM_edge_other_vert(eed, eve);
1653                                                 if (BM_elem_flag_test(eve_other, BM_ELEM_TAG)) {
1654                                                         /* this is a tagged vert we didnt walk over yet, step onto it */
1655                                                         eve_next = eve_other;
1656                                                         break;
1657                                                 }
1658                                         }
1659                                 }
1660
1661                                 eve = eve_next;
1662                         }
1663
1664                         /* now we have all verts, make into a line */
1665                         if (BLI_array_count(eve_line) > 2) {
1666
1667                                 /* we know the returns from these must be valid */
1668                                 float *uv_start = uv_sel_co_from_eve(scene, ima, em, eve_line[0]);
1669                                 float *uv_end   = uv_sel_co_from_eve(scene, ima, em, eve_line[BLI_array_count(eve_line) - 1]);
1670                                 /* For t & u modes */
1671                                 float a = 0.0f;
1672
1673                                 if (tool == 't') {
1674                                         if (uv_start[1] == uv_end[1])
1675                                                 tool = 's';
1676                                         else
1677                                                 a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
1678                                 }
1679                                 else if (tool == 'u') {
1680                                         if (uv_start[0] == uv_end[0])
1681                                                 tool = 's';
1682                                         else
1683                                                 a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
1684                                 }
1685
1686                                 /* go over all verts except for endpoints */
1687                                 for (i = 0; i < BLI_array_count(eve_line); i++) {
1688                                         BM_ITER_ELEM (l, &liter, eve_line[i], BM_LOOPS_OF_VERT) {
1689                                                 tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1690
1691                                                 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1692                                                         continue;
1693
1694                                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1695                                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1696                                                         /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
1697                                                          * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
1698                                                          * Maybe this should be a BLI func? Or is it already existing?
1699                                                          * Could use interp_v2_v2v2, but not sure it's worth it here...*/
1700                                                         if (tool == 't')
1701                                                                 luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0];
1702                                                         else if (tool == 'u')
1703                                                                 luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1];
1704                                                         else
1705                                                                 closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end);
1706                                                 }
1707                                         }
1708                                 }
1709                         }
1710                         else {
1711                                 /* error - not a line, needs 3+ points  */
1712                         }
1713
1714                         if (eve_line) {
1715                                 MEM_freeN(eve_line);
1716                         }
1717                 }
1718                 else {
1719                         /* error - cant find an endpoint */
1720                 }
1721         }
1722
1723
1724         uvedit_live_unwrap_update(sima, scene, obedit);
1725         DAG_id_tag_update(obedit->data, 0);
1726         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1727 }
1728
1729 static int uv_align_exec(bContext *C, wmOperator *op)
1730 {
1731         uv_weld_align(C, RNA_enum_get(op->ptr, "axis"));
1732
1733         return OPERATOR_FINISHED;
1734 }
1735
1736 static void UV_OT_align(wmOperatorType *ot)
1737 {
1738         static EnumPropertyItem axis_items[] = {
1739                 {'s', "ALIGN_S", 0, "Straighten", "Align UVs along the line defined by the endpoints"},
1740                 {'t', "ALIGN_T", 0, "Straighten X", "Align UVs along the line defined by the endpoints along the X axis"},
1741                 {'u', "ALIGN_U", 0, "Straighten Y", "Align UVs along the line defined by the endpoints along the Y axis"},
1742                 {'a', "ALIGN_AUTO", 0, "Align Auto", "Automatically choose the axis on which there is most alignment already"},
1743                 {'x', "ALIGN_X", 0, "Align X", "Align UVs on X axis"},
1744                 {'y', "ALIGN_Y", 0, "Align Y", "Align UVs on Y axis"},
1745                 {0, NULL, 0, NULL, NULL}};
1746
1747         /* identifiers */
1748         ot->name = "Align";
1749         ot->description = "Align selected UV vertices to an axis";
1750         ot->idname = "UV_OT_align";
1751         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1752         
1753         /* api callbacks */
1754         ot->exec = uv_align_exec;
1755         ot->poll = ED_operator_uvedit;
1756
1757         /* properties */
1758         RNA_def_enum(ot->srna, "axis", axis_items, 'a', "Axis", "Axis to align UV locations on");
1759 }
1760 /* ******************** weld near operator **************** */
1761
1762 typedef struct UVvert {
1763         MLoopUV *uv_loop;
1764         int weld;
1765 } UVvert;
1766
1767 static int uv_remove_doubles_exec(bContext *C, wmOperator *op)
1768 {
1769         const float threshold = RNA_float_get(op->ptr, "threshold");
1770         const int use_unselected = RNA_boolean_get(op->ptr, "use_unselected");
1771
1772         SpaceImage *sima;
1773         Scene *scene;
1774         Object *obedit = CTX_data_edit_object(C);
1775         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1776         Image *ima;
1777         MTexPoly *tf;
1778         int uv_a_index;
1779         int uv_b_index;
1780         float *uv_a;
1781         float *uv_b;
1782
1783         BMIter iter, liter;
1784         BMFace *efa;
1785         BMLoop *l;
1786
1787         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1788         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1789
1790         sima = CTX_wm_space_image(C);
1791         scene = CTX_data_scene(C);
1792         ima = CTX_data_edit_image(C);
1793
1794         if (use_unselected == FALSE) {
1795                 UVvert *vert_arr = NULL;
1796                 BLI_array_declare(vert_arr);
1797                 MLoopUV **loop_arr = NULL;
1798                 BLI_array_declare(loop_arr);
1799
1800                 /* TODO, use qsort as with MESH_OT_remove_doubles, this isn't optimal */
1801                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1802                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1803                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1804                                 continue;
1805
1806                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1807                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1808                                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1809                                         UVvert vert;
1810                                         vert.uv_loop = luv;
1811                                         vert.weld = FALSE;
1812                                         BLI_array_append(vert_arr, vert);
1813                                 }
1814
1815                         }
1816                 }
1817
1818                 for (uv_a_index = 0; uv_a_index < BLI_array_count(vert_arr); uv_a_index++) {
1819                         if (vert_arr[uv_a_index].weld == FALSE) {
1820                                 float uv_min[2];
1821                                 float uv_max[2];
1822
1823                                 BLI_array_empty(loop_arr);
1824                                 BLI_array_append(loop_arr, vert_arr[uv_a_index].uv_loop);
1825
1826                                 uv_a = vert_arr[uv_a_index].uv_loop->uv;
1827
1828                                 copy_v2_v2(uv_max, uv_a);
1829                                 copy_v2_v2(uv_min, uv_a);
1830
1831                                 vert_arr[uv_a_index].weld = TRUE;
1832                                 for (uv_b_index = uv_a_index + 1; uv_b_index < BLI_array_count(vert_arr); uv_b_index++) {
1833                                         uv_b = vert_arr[uv_b_index].uv_loop->uv;
1834                                         if ((vert_arr[uv_b_index].weld == FALSE) &&
1835                                             (len_manhattan_v2v2(uv_a, uv_b) < threshold))
1836                                         {
1837                                                 minmax_v2v2_v2(uv_max, uv_min, uv_b);
1838                                                 BLI_array_append(loop_arr, vert_arr[uv_b_index].uv_loop);
1839                                                 vert_arr[uv_b_index].weld = TRUE;
1840                                         }
1841                                 }
1842                                 if (BLI_array_count(loop_arr)) {
1843                                         float uv_mid[2];
1844                                         mid_v2_v2v2(uv_mid, uv_min, uv_max);
1845                                         for (uv_b_index = 0; uv_b_index < BLI_array_count(loop_arr); uv_b_index++) {
1846                                                 copy_v2_v2(loop_arr[uv_b_index]->uv, uv_mid);
1847                                         }
1848                                 }
1849                         }
1850                 }
1851
1852                 BLI_array_free(vert_arr);
1853                 BLI_array_free(loop_arr);
1854         }
1855         else {
1856                 /* selected -> unselected
1857                  *
1858                  * No need to use 'UVvert' here */
1859                 MLoopUV **loop_arr = NULL;
1860                 BLI_array_declare(loop_arr);
1861                 MLoopUV **loop_arr_unselected = NULL;
1862                 BLI_array_declare(loop_arr_unselected);
1863
1864                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1865                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1866                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
1867                                 continue;
1868
1869                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1870                                 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1871                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1872                                         BLI_array_append(loop_arr, luv);
1873                                 }
1874                                 else {
1875                                         BLI_array_append(loop_arr_unselected, luv);
1876                                 }
1877                         }
1878                 }
1879
1880                 for (uv_a_index = 0; uv_a_index < BLI_array_count(loop_arr); uv_a_index++) {
1881                         float dist_best = FLT_MAX, dist;
1882                         float *uv_best = NULL;
1883
1884                         uv_a = loop_arr[uv_a_index]->uv;
1885                         for (uv_b_index = 0; uv_b_index < BLI_array_count(loop_arr_unselected); uv_b_index++) {
1886                                 uv_b = loop_arr_unselected[uv_b_index]->uv;
1887                                 dist = len_manhattan_v2v2(uv_a, uv_b);
1888                                 if ((dist < threshold) && (dist < dist_best)) {
1889                                         uv_best = uv_b;
1890                                         dist_best = dist;
1891                                 }
1892                         }
1893                         if (uv_best) {
1894                                 copy_v2_v2(uv_a, uv_best);
1895                         }
1896                 }
1897
1898                 BLI_array_free(loop_arr);
1899                 BLI_array_free(loop_arr_unselected);
1900         }
1901
1902         uvedit_live_unwrap_update(sima, scene, obedit);
1903         DAG_id_tag_update(obedit->data, 0);
1904         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1905
1906         return OPERATOR_FINISHED;
1907 }
1908
1909 static void UV_OT_remove_doubles(wmOperatorType *ot)
1910 {
1911         /* identifiers */
1912         ot->name = "Remove Doubles UV";
1913         ot->description = "Selected UV vertices that are within a radius of each other are welded together";
1914         ot->idname = "UV_OT_remove_doubles";
1915         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1916
1917         /* api callbacks */
1918         ot->exec = uv_remove_doubles_exec;
1919         ot->poll = ED_operator_uvedit;
1920
1921         RNA_def_float(ot->srna, "threshold", 0.02f, 0.0f, 10.0f,
1922                       "Merge Distance", "Maximum distance between welded vertices", 0.0f, 1.0f);
1923         RNA_def_boolean(ot->srna, "use_unselected", 0, "Unselected", "Merge selected to other unselected vertices");
1924 }
1925 /* ******************** weld operator **************** */
1926
1927 static int uv_weld_exec(bContext *C, wmOperator *UNUSED(op))
1928 {
1929         uv_weld_align(C, 'w');
1930
1931         return OPERATOR_FINISHED;
1932 }
1933
1934 static void UV_OT_weld(wmOperatorType *ot)
1935 {
1936         /* identifiers */
1937         ot->name = "Weld";
1938         ot->description = "Weld selected UV vertices together";
1939         ot->idname = "UV_OT_weld";
1940         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1941         
1942         /* api callbacks */
1943         ot->exec = uv_weld_exec;
1944         ot->poll = ED_operator_uvedit;
1945 }
1946
1947
1948 /* ******************** (de)select all operator **************** */
1949
1950 static void uv_select_all_perform(Scene *scene, Image *ima, BMEditMesh *em, int action)
1951 {
1952         ToolSettings *ts = scene->toolsettings;
1953         BMFace *efa;
1954         BMLoop *l;
1955         BMIter iter, liter;
1956         MTexPoly *tf;
1957         MLoopUV *luv;
1958
1959         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1960         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1961
1962         if (ts->uv_flag & UV_SYNC_SELECTION) {
1963
1964                 switch (action) {
1965                         case SEL_TOGGLE:
1966                                 EDBM_select_toggle_all(em);
1967                                 break;
1968                         case SEL_SELECT:
1969                                 EDBM_flag_enable_all(em, BM_ELEM_SELECT);
1970                                 break;
1971                         case SEL_DESELECT:
1972                                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1973                                 break;
1974                         case SEL_INVERT:
1975                                 EDBM_select_swap(em);
1976                                 EDBM_selectmode_flush(em);
1977                                 break;
1978                 }
1979         }
1980         else {
1981                 if (action == SEL_TOGGLE) {
1982                         action = SEL_SELECT;
1983                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1984                                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1985         
1986                                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
1987                                         continue;
1988
1989                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1990                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1991
1992                                         if (luv->flag & MLOOPUV_VERTSEL) {
1993                                                 action = SEL_DESELECT;
1994                                                 break;
1995                                         }
1996                                 }
1997                         }
1998                 }
1999         
2000                 
2001                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2002                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2003
2004                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
2005                                 continue;
2006
2007                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2008                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2009
2010                                 switch (action) {
2011                                         case SEL_SELECT:
2012                                                 luv->flag |= MLOOPUV_VERTSEL;
2013                                                 break;
2014                                         case SEL_DESELECT:
2015                                                 luv->flag &= ~MLOOPUV_VERTSEL;
2016                                                 break;
2017                                         case SEL_INVERT:
2018                                                 luv->flag ^= MLOOPUV_VERTSEL;
2019                                                 break;
2020                                 }
2021                         }
2022                 }
2023         }
2024 }
2025
2026 static int uv_select_all_exec(bContext *C, wmOperator *op)
2027 {
2028         Scene *scene = CTX_data_scene(C);
2029         Object *obedit = CTX_data_edit_object(C);
2030         Image *ima = CTX_data_edit_image(C);
2031         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2032
2033         int action = RNA_enum_get(op->ptr, "action");
2034
2035         uv_select_all_perform(scene, ima, em, action);
2036
2037         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2038
2039         return OPERATOR_FINISHED;
2040 }
2041
2042 static void UV_OT_select_all(wmOperatorType *ot)
2043 {
2044         /* identifiers */
2045         ot->name = "(De)select All";
2046         ot->description = "Change selection of all UV vertices";
2047         ot->idname = "UV_OT_select_all";
2048         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2049         
2050         /* api callbacks */
2051         ot->exec = uv_select_all_exec;
2052         ot->poll = ED_operator_uvedit;
2053
2054         WM_operator_properties_select_all(ot);
2055 }
2056
2057 /* ******************** mouse select operator **************** */
2058
2059 static bool uv_sticky_select(float *limit, int hitv[4], int v, float *hituv[4], float *uv, int sticky, int hitlen)
2060 {
2061         int i;
2062
2063         /* this function test if some vertex needs to selected
2064          * in addition to the existing ones due to sticky select */
2065         if (sticky == SI_STICKY_DISABLE)
2066                 return false;
2067
2068         for (i = 0; i < hitlen; i++) {
2069                 if (hitv[i] == v) {
2070                         if (sticky == SI_STICKY_LOC) {
2071                                 if (fabsf(hituv[i][0] - uv[0]) < limit[0] && fabsf(hituv[i][1] - uv[1]) < limit[1])
2072                                         return true;
2073                         }
2074                         else if (sticky == SI_STICKY_VERTEX)
2075                                 return true;
2076                 }
2077         }
2078
2079         return false;
2080 }
2081
2082 static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loop)
2083 {
2084         SpaceImage *sima = CTX_wm_space_image(C);
2085         Scene *scene = CTX_data_scene(C);
2086         ToolSettings *ts = scene->toolsettings;
2087         Object *obedit = CTX_data_edit_object(C);
2088         Image *ima = CTX_data_edit_image(C);
2089         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2090         BMFace *efa;
2091         BMLoop *l;
2092         BMIter iter, liter;
2093         MTexPoly *tf;
2094         MLoopUV *luv;
2095         NearestHit hit;
2096         int i, selectmode, sticky, sync, *hitv = NULL;
2097         bool select = true;
2098         BLI_array_declare(hitv);
2099         int flush = 0, hitlen = 0; /* 0 == don't flush, 1 == sel, -1 == desel;  only use when selection sync is enabled */
2100         float limit[2], **hituv = NULL;
2101         BLI_array_declare(hituv);
2102         float penalty[2];
2103
2104         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2105         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2106
2107         /* notice 'limit' is the same no matter the zoom level, since this is like
2108          * remove doubles and could annoying if it joined points when zoomed out.
2109          * 'penalty' is in screen pixel space otherwise zooming in on a uv-vert and
2110          * shift-selecting can consider an adjacent point close enough to add to
2111          * the selection rather than de-selecting the closest. */
2112
2113         uvedit_pixel_to_float(sima, limit, 0.05f);
2114         uvedit_pixel_to_float(sima, penalty, 5.0f / (sima ? sima->zoom : 1.0f));
2115
2116         /* retrieve operation mode */
2117         if (ts->uv_flag & UV_SYNC_SELECTION) {
2118                 sync = 1;
2119
2120                 if (ts->selectmode & SCE_SELECT_FACE)
2121                         selectmode = UV_SELECT_FACE;
2122                 else if (ts->selectmode & SCE_SELECT_EDGE)
2123                         selectmode = UV_SELECT_EDGE;
2124                 else
2125                         selectmode = UV_SELECT_VERTEX;
2126
2127                 sticky = SI_STICKY_DISABLE;
2128         }
2129         else {
2130                 sync = 0;
2131                 selectmode = ts->uv_selectmode;
2132                 sticky = (sima) ? sima->sticky : 1;
2133         }
2134
2135         /* find nearest element */
2136         if (loop) {
2137                 /* find edge */
2138                 uv_find_nearest_edge(scene, ima, em, co, &hit);
2139                 if (hit.efa == NULL) {
2140                         BLI_array_free(hitv);
2141                         BLI_array_free(hituv);
2142                         return OPERATOR_CANCELLED;
2143                 }
2144
2145                 hitlen = 0;
2146         }
2147         else if (selectmode == UV_SELECT_VERTEX) {
2148                 /* find vertex */
2149                 uv_find_nearest_vert(scene, ima, em, co, penalty, &hit);
2150                 if (hit.efa == NULL) {
2151                         BLI_array_free(hitv);
2152                         BLI_array_free(hituv);
2153                         return OPERATOR_CANCELLED;
2154                 }
2155
2156                 /* mark 1 vertex as being hit */
2157                 BLI_array_grow_items(hitv, hit.efa->len);
2158                 BLI_array_grow_items(hituv, hit.efa->len);
2159                 for (i = 0; i < hit.efa->len; i++) {
2160                         hitv[i] = 0xFFFFFFFF;
2161                 }
2162
2163                 hitv[hit.lindex] = hit.vert1;
2164                 hituv[hit.lindex] = hit.luv->uv;
2165
2166                 hitlen = hit.efa->len;
2167         }
2168         else if (selectmode == UV_SELECT_EDGE) {
2169                 /* find edge */
2170                 uv_find_nearest_edge(scene, ima, em, co, &hit);
2171                 if (hit.efa == NULL) {
2172                         BLI_array_free(hitv);
2173                         BLI_array_free(hituv);
2174                         return OPERATOR_CANCELLED;
2175                 }
2176
2177                 /* mark 2 edge vertices as being hit */
2178                 BLI_array_grow_items(hitv,  hit.efa->len);
2179                 BLI_array_grow_items(hituv, hit.efa->len);
2180                 fill_vn_i(hitv, hit.efa->len, 0xFFFFFFFF);
2181
2182                 hitv[hit.lindex] = hit.vert1;
2183                 hitv[(hit.lindex + 1) % hit.efa->len] = hit.vert2;
2184                 hituv[hit.lindex] = hit.luv->uv;
2185                 hituv[(hit.lindex + 1) % hit.efa->len] = hit.luv_next->uv;
2186
2187                 hitlen = hit.efa->len;
2188         }
2189         else if (selectmode == UV_SELECT_FACE) {
2190                 /* find face */
2191                 uv_find_nearest_face(scene, ima, em, co, &hit);
2192                 if (hit.efa == NULL) {
2193                         BLI_array_free(hitv);
2194                         BLI_array_free(hituv);
2195                         return OPERATOR_CANCELLED;
2196                 }
2197                 
2198                 /* make active */
2199                 BM_active_face_set(em->bm, hit.efa);
2200
2201                 /* mark all face vertices as being hit */
2202
2203                 BLI_array_grow_items(hitv,  hit.efa->len);
2204                 BLI_array_grow_items(hituv, hit.efa->len);
2205                 i = 0;
2206                 BM_ITER_ELEM (l, &liter, hit.efa, BM_LOOPS_OF_FACE) {
2207                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2208                         hituv[i] = luv->uv;
2209                         hitv[i] = BM_elem_index_get(l->v);
2210                         i++;
2211                 }
2212                 
2213                 hitlen = hit.efa->len;
2214         }
2215         else if (selectmode == UV_SELECT_ISLAND) {
2216                 uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
2217
2218                 if (hit.efa == NULL) {
2219                         BLI_array_free(hitv);
2220                         BLI_array_free(hituv);
2221                         return OPERATOR_CANCELLED;
2222                 }
2223
2224                 hitlen = 0;
2225         }
2226         else {
2227                 hitlen = 0;
2228                 BLI_array_free(hitv);
2229                 BLI_array_free(hituv);
2230                 return OPERATOR_CANCELLED;
2231         }
2232
2233         /* do selection */
2234         if (loop) {
2235                 flush = uv_select_edgeloop(scene, ima, em, &hit, limit, extend);
2236         }
2237         else if (selectmode == UV_SELECT_ISLAND) {
2238                 uv_select_linked(scene, ima, em, limit, &hit, extend);
2239         }
2240         else if (extend) {
2241                 if (selectmode == UV_SELECT_VERTEX) {
2242                         /* (de)select uv vertex */
2243                         select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
2244                         uvedit_uv_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset);
2245                         flush = 1;
2246                 }
2247                 else if (selectmode == UV_SELECT_EDGE) {
2248                         /* (de)select edge */
2249                         select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset));
2250                         uvedit_edge_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset);
2251                         flush = 1;
2252                 }
2253                 else if (selectmode == UV_SELECT_FACE) {
2254                         /* (de)select face */
2255                         select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset));
2256                         uvedit_face_select_set(scene, em, hit.efa, select, true, cd_loop_uv_offset);
2257                         flush = -1;
2258                 }
2259
2260                 /* de-selecting an edge may deselect a face too - validate */
2261                 if (sync) {
2262                         if (select == FALSE) {
2263                                 BM_select_history_validate(em->bm);
2264                         }
2265                 }
2266
2267                 /* (de)select sticky uv nodes */
2268                 if (sticky != SI_STICKY_DISABLE) {
2269
2270                         BM_mesh_elem_index_ensure(em->bm, BM_VERT);
2271
2272                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2273                                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2274                                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2275                                         continue;
2276
2277                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2278                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2279                                         if (uv_sticky_select(limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen))
2280                                                 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2281                                 }
2282                         }
2283
2284                         flush = select ? 1 : -1;
2285                 }
2286         }
2287         else {
2288                 /* deselect all */
2289                 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
2290
2291                 if (selectmode == UV_SELECT_VERTEX) {
2292                         /* select vertex */
2293                         uvedit_uv_select_enable(em, scene, hit.l, true, cd_loop_uv_offset);
2294                         flush = 1;
2295                 }
2296                 else if (selectmode == UV_SELECT_EDGE) {
2297                         /* select edge */
2298                         uvedit_edge_select_enable(em, scene, hit.l, true, cd_loop_uv_offset);
2299                         flush = 1;
2300                 }
2301                 else if (selectmode == UV_SELECT_FACE) {
2302                         /* select face */
2303                         uvedit_face_select_enable(scene, em, hit.efa, true, cd_loop_uv_offset);
2304                 }
2305
2306                 /* select sticky uvs */
2307                 if (sticky != SI_STICKY_DISABLE) {
2308                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2309                                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2310                                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2311                                         continue;
2312                                 
2313                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2314                                         if (sticky == SI_STICKY_DISABLE) continue;
2315                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2316
2317                                         if (uv_sticky_select(limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen))
2318                                                 uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset);
2319
2320                                         flush = 1;
2321                                 }
2322                         }
2323                 }
2324         }
2325
2326         if (sync) {
2327                 /* flush for mesh selection */
2328
2329                 /* before bmesh */
2330 #if 0
2331                 if (ts->selectmode != SCE_SELECT_FACE) {
2332                         if (flush == 1) EDBM_select_flush(em);
2333                         else if (flush == -1) EDBM_deselect_flush(em);
2334                 }
2335 #else
2336                 if (flush != 0) {
2337                         if (loop) {
2338                                 /* push vertex -> edge selection */
2339                                 if (select) {
2340                                         EDBM_select_flush(em);
2341                                 }
2342                                 else {
2343                                         EDBM_deselect_flush(em);
2344                                 }
2345                         }
2346                         else {
2347                                 EDBM_selectmode_flush(em);
2348                         }
2349                 }
2350 #endif
2351         }
2352
2353         DAG_id_tag_update(obedit->data, 0);
2354         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2355
2356         BLI_array_free(hitv);
2357         BLI_array_free(hituv);
2358
2359         return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
2360 }
2361
2362 static int uv_select_exec(bContext *C, wmOperator *op)
2363 {
2364         float co[2];
2365         bool extend, loop;
2366
2367         RNA_float_get_array(op->ptr, "location", co);
2368         extend = RNA_boolean_get(op->ptr, "extend");
2369         loop = false;
2370
2371         return uv_mouse_select(C, co, extend, loop);
2372 }
2373
2374 static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2375 {
2376         ARegion *ar = CTX_wm_region(C);
2377         float co[2];
2378
2379         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2380         RNA_float_set_array(op->ptr, "location", co);
2381
2382         return uv_select_exec(C, op);
2383 }
2384
2385 static void UV_OT_select(wmOperatorType *ot)
2386 {
2387         /* identifiers */
2388         ot->name = "Select";
2389         ot->description = "Select UV vertices";
2390         ot->idname = "UV_OT_select";
2391         ot->flag = OPTYPE_UNDO;
2392         
2393         /* api callbacks */
2394         ot->exec = uv_select_exec;
2395         ot->invoke = uv_select_invoke;
2396         ot->poll = ED_operator_uvedit; /* requires space image */;
2397
2398         /* properties */
2399         RNA_def_boolean(ot->srna, "extend", 0,
2400                         "Extend", "Extend selection rather than clearing the existing selection");
2401         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2402                              "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2403 }
2404
2405 /* ******************** loop select operator **************** */
2406
2407 static int uv_select_loop_exec(bContext *C, wmOperator *op)
2408 {
2409         float co[2];
2410         bool extend, loop;
2411
2412         RNA_float_get_array(op->ptr, "location", co);
2413         extend = RNA_boolean_get(op->ptr, "extend");
2414         loop = true;
2415
2416         return uv_mouse_select(C, co, extend, loop);
2417 }
2418
2419 static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2420 {
2421         ARegion *ar = CTX_wm_region(C);
2422         float co[2];
2423
2424         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2425         RNA_float_set_array(op->ptr, "location", co);
2426
2427         return uv_select_loop_exec(C, op);
2428 }
2429
2430 static void UV_OT_select_loop(wmOperatorType *ot)
2431 {
2432         /* identifiers */
2433         ot->name = "Loop Select";
2434         ot->description = "Select a loop of connected UV vertices";
2435         ot->idname = "UV_OT_select_loop";
2436         ot->flag = OPTYPE_UNDO;
2437         
2438         /* api callbacks */
2439         ot->exec = uv_select_loop_exec;
2440         ot->invoke = uv_select_loop_invoke;
2441         ot->poll = ED_operator_uvedit; /* requires space image */;
2442
2443         /* properties */
2444         RNA_def_boolean(ot->srna, "extend", 0,
2445                         "Extend", "Extend selection rather than clearing the existing selection");
2446         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2447                              "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2448 }
2449
2450 /* ******************** linked select operator **************** */
2451
2452 static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, int pick)
2453 {
2454         SpaceImage *sima = CTX_wm_space_image(C);
2455         Scene *scene = CTX_data_scene(C);
2456         ToolSettings *ts = scene->toolsettings;
2457         Object *obedit = CTX_data_edit_object(C);
2458         Image *ima = CTX_data_edit_image(C);
2459         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2460         float limit[2];
2461         int extend;
2462
2463         NearestHit hit, *hit_p = NULL;
2464
2465         if (ts->uv_flag & UV_SYNC_SELECTION) {
2466                 BKE_report(op->reports, RPT_ERROR, "Cannot select linked when sync selection is enabled");
2467                 return OPERATOR_CANCELLED;
2468         }
2469
2470         extend = RNA_boolean_get(op->ptr, "extend");
2471         uvedit_pixel_to_float(sima, limit, 0.05f);
2472
2473         if (pick) {
2474                 float co[2];
2475
2476                 if (event) {
2477                         /* invoke */
2478                         ARegion *ar = CTX_wm_region(C);
2479
2480                         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2481                         RNA_float_set_array(op->ptr, "location", co);
2482                 }
2483                 else {
2484                         /* exec */
2485                         RNA_float_get_array(op->ptr, "location", co);
2486                 }
2487
2488                 uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
2489                 hit_p = &hit;
2490         }
2491
2492         uv_select_linked(scene, ima, em, limit, hit_p, extend);
2493
2494         DAG_id_tag_update(obedit->data, 0);
2495         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2496
2497         return OPERATOR_FINISHED;
2498 }
2499
2500 static int uv_select_linked_exec(bContext *C, wmOperator *op)
2501 {
2502         return uv_select_linked_internal(C, op, NULL, 0);
2503 }
2504
2505 static void UV_OT_select_linked(wmOperatorType *ot)
2506 {
2507         /* identifiers */
2508         ot->name = "Select Linked";
2509         ot->description = "Select all UV vertices linked to the active UV map";
2510         ot->idname = "UV_OT_select_linked";
2511         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2512         
2513         /* api callbacks */
2514         ot->exec = uv_select_linked_exec;
2515         ot->poll = ED_operator_uvedit;    /* requires space image */
2516
2517         /* properties */
2518         RNA_def_boolean(ot->srna, "extend", 0,
2519                         "Extend", "Extend selection rather than clearing the existing selection");
2520 }
2521
2522 static int uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2523 {
2524         return uv_select_linked_internal(C, op, event, 1);
2525 }
2526
2527 static int uv_select_linked_pick_exec(bContext *C, wmOperator *op)
2528 {
2529         return uv_select_linked_internal(C, op, NULL, 1);
2530 }
2531
2532 static void UV_OT_select_linked_pick(wmOperatorType *ot)
2533 {
2534         /* identifiers */
2535         ot->name = "Select Linked Pick";
2536         ot->description = "Select all UV vertices linked under the mouse";
2537         ot->idname = "UV_OT_select_linked_pick";
2538         ot->flag = OPTYPE_UNDO;
2539
2540         /* api callbacks */
2541         ot->invoke = uv_select_linked_pick_invoke;
2542         ot->exec = uv_select_linked_pick_exec;
2543         ot->poll = ED_operator_uvedit; /* requires space image */;
2544
2545         /* properties */
2546         RNA_def_boolean(ot->srna, "extend", 0,
2547                         "Extend", "Extend selection rather than clearing the existing selection");
2548
2549         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2550                              "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2551 }
2552
2553 /* note: this is based on similar use case to MESH_OT_split(), which has a similar effect
2554  * but in this case they are not joined to begin with (only having the behavior of being joined)
2555  * so its best to call this uv_select_split() instead of just split(), but assigned to the same key
2556  * as MESH_OT_split - Campbell */
2557 static int uv_select_split_exec(bContext *C, wmOperator *op)
2558 {
2559         Scene *scene = CTX_data_scene(C);
2560         ToolSettings *ts = scene->toolsettings;
2561         Image *ima = CTX_data_edit_image(C);
2562         Object *obedit = CTX_data_edit_object(C);
2563         BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2564
2565         BMFace *efa;
2566         BMLoop *l;
2567         BMIter iter, liter;
2568         MTexPoly *tf;
2569         MLoopUV *luv;
2570         short change = FALSE;
2571
2572         const int cd_loop_uv_offset  = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
2573         const int cd_poly_tex_offset = CustomData_get_offset(&bm->pdata, CD_MTEXPOLY);
2574
2575         if (ts->uv_flag & UV_SYNC_SELECTION) {
2576                 BKE_report(op->reports, RPT_ERROR, "Cannot split selection when sync selection is enabled");
2577                 return OPERATOR_CANCELLED;
2578         }
2579
2580
2581
2582         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2583                 int is_sel = FALSE;
2584                 int is_unsel = FALSE;
2585                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2586
2587                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2588                         continue;
2589
2590                 /* are we all selected? */
2591                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2592                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2593
2594                         if (luv->flag & MLOOPUV_VERTSEL) {
2595                                 is_sel = TRUE;
2596                         }
2597                         else {
2598                                 is_unsel = TRUE;
2599                         }
2600
2601                         /* we have mixed selection, bail out */
2602                         if (is_sel && is_unsel) {
2603                                 break;
2604                         }
2605                 }
2606
2607                 if (is_sel && is_unsel) {
2608                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2609                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2610                                 luv->flag &= ~MLOOPUV_VERTSEL;
2611                         }
2612
2613                         change = TRUE;
2614                 }
2615         }
2616
2617         if (change) {
2618                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
2619                 return OPERATOR_FINISHED;
2620         }
2621         else {
2622                 return OPERATOR_CANCELLED;
2623         }
2624 }
2625
2626
2627 static void UV_OT_select_split(wmOperatorType *ot)
2628 {
2629         /* identifiers */
2630         ot->name = "Select Split";
2631         ot->description = "Select only entirely selected faces";
2632         ot->idname = "UV_OT_select_split";
2633         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2634
2635         /* api callbacks */
2636         ot->exec = uv_select_split_exec;
2637         ot->poll = ED_operator_uvedit; /* requires space image */;
2638 }
2639
2640 /* ******************** unlink selection operator **************** */
2641
2642 static int uv_unlink_selection_exec(bContext *C, wmOperator *op)
2643 {
2644         Scene *scene = CTX_data_scene(C);
2645         ToolSettings *ts = scene->toolsettings;
2646         Object *obedit = CTX_data_edit_object(C);
2647         Image *ima = CTX_data_edit_image(C);
2648         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2649         BMFace *efa;
2650         BMLoop *l;
2651         BMIter iter, liter;
2652         MTexPoly *tf;
2653         MLoopUV *luv;
2654
2655         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2656         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2657
2658         if (ts->uv_flag & UV_SYNC_SELECTION) {
2659                 BKE_report(op->reports, RPT_ERROR, "Cannot unlink selection when sync selection is enabled");
2660                 return OPERATOR_CANCELLED;
2661         }
2662         
2663         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2664                 int desel = 0;
2665
2666                 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2667                 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2668                         continue;
2669
2670                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2671                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2672                         
2673                         if (!(luv->flag & MLOOPUV_VERTSEL)) {
2674                                 desel = 1;
2675                                 break;
2676                         }
2677                 }
2678
2679                 if (desel) {
2680                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2681                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2682                                 luv->flag &= ~MLOOPUV_VERTSEL;
2683                         }
2684                 }
2685         }
2686         
2687         DAG_id_tag_update(obedit->data, 0);
2688         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2689
2690         return OPERATOR_FINISHED;
2691 }
2692
2693 static void UV_OT_unlink_selected(wmOperatorType *ot)
2694 {
2695         /* identifiers */
2696         ot->name = "Unlink Selection";
2697         ot->description = "Unlink selected UV vertices from active UV map";
2698         ot->idname = "UV_OT_unlink_selected";
2699         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2700         
2701         /* api callbacks */
2702         ot->exec = uv_unlink_selection_exec;
2703         ot->poll = ED_operator_uvedit;
2704 }
2705
2706 static void uv_select_sync_flush(ToolSettings *ts, BMEditMesh *em, const short select)
2707 {
2708         /* bmesh API handles flushing but not on de-select */
2709         if (ts->uv_flag & UV_SYNC_SELECTION) {
2710                 if (ts->selectmode != SCE_SELECT_FACE) {
2711                         if (select == FALSE) {
2712                                 EDBM_deselect_flush(em);
2713                         }
2714                         else {
2715                                 EDBM_select_flush(em);
2716                         }
2717                 }
2718
2719                 if (select == FALSE) {
2720                         BM_select_history_validate(em->bm);
2721                 }
2722         }
2723 }
2724
2725
2726
2727 /* -------------------------------------------------------------------- */
2728 /* Utility functions to flush the uv-selection from tags */
2729
2730 /**
2731  * helper function for #uv_select_flush_from_tag_loop and uv_select_flush_from_tag_face
2732  */
2733 static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene, BMEditMesh *em, UvVertMap *vmap,
2734                                                          const unsigned int efa_index, BMLoop *l,
2735                                                          const bool select, const int cd_loop_uv_offset)
2736 {
2737         UvMapVert *start_vlist = NULL, *vlist_iter;
2738         BMFace *efa_vlist;
2739
2740         uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2741
2742         vlist_iter = EDBM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
2743
2744         while (vlist_iter) {
2745                 if (vlist_iter->separate)
2746                         start_vlist = vlist_iter;
2747
2748                 if (efa_index == vlist_iter->f)
2749                         break;
2750
2751                 vlist_iter = vlist_iter->next;
2752         }
2753
2754         vlist_iter = start_vlist;
2755         while (vlist_iter) {
2756
2757                 if (vlist_iter != start_vlist && vlist_iter->separate)
2758                         break;
2759
2760                 if (efa_index != vlist_iter->f) {
2761                         BMLoop *l_other;
2762                         efa_vlist = EDBM_face_at_index(em, vlist_iter->f);
2763                         /* tf_vlist = BM_ELEM_CD_GET_VOID_P(efa_vlist, cd_poly_tex_offset); */ /* UNUSED */
2764
2765                         l_other = BM_iter_at_index(em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->tfindex);
2766
2767                         uvedit_uv_select_set(em, scene, l_other, select, false, cd_loop_uv_offset);
2768                 }
2769                 vlist_iter = vlist_iter->next;
2770         }
2771 }
2772
2773 /**
2774  * Flush the selection from face tags based on sticky and selection modes.
2775  *
2776  * needed because settings the selection a face is done in a number of places but it also needs to respect
2777  * the sticky modes for the UV verts, so dealing with the sticky modes is best done in a separate function.
2778  *
2779  * \note! This function is very similar to #uv_select_flush_from_tag_loop, be sure to update both upon changing.
2780  */
2781 static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object *obedit, const bool select)
2782 {
2783         /* Selecting UV Faces with some modes requires us to change 
2784          * the selection in other faces (depending on the sticky mode).
2785          * 
2786          * This only needs to be done when the Mesh is not used for
2787          * selection (so for sticky modes, vertex or location based). */
2788         
2789         ToolSettings *ts = scene->toolsettings;
2790         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2791         BMFace *efa;
2792         BMLoop *l;
2793         BMIter iter, liter;
2794         /* MTexPoly *tf; */
2795         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2796         
2797         if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
2798                 /* Tag all verts as untouched, then touch the ones that have a face center
2799                  * in the loop and select all MLoopUV's that use a touched vert. */
2800                 BMVert *eve;
2801                 
2802                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2803                         BM_elem_flag_disable(eve, BM_ELEM_TAG);
2804                 }
2805                 
2806                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2807                         if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2808                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2809                                         BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2810                                 }
2811                         }
2812                 }
2813
2814                 /* now select tagged verts */
2815                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2816                         /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2817
2818                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2819                                 if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
2820                                         uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2821                                 }
2822                         }
2823                 }
2824         }
2825         else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
2826                 struct UvVertMap *vmap;
2827                 float limit[2];
2828                 unsigned int efa_index;
2829                 
2830                 uvedit_pixel_to_float(sima, limit, 0.05);
2831                 
2832                 EDBM_index_arrays_ensure(em, BM_FACE);
2833                 vmap = EDBM_uv_vert_map_create(em, 0, limit);
2834                 if (vmap == NULL) {
2835                         return;
2836                 }
2837                 
2838                 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
2839                         if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2840                                 /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2841                                 
2842                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2843                                         uv_select_flush_from_tag_sticky_loc_internal(scene, em, vmap, efa_index, l,
2844                                                                                      select, cd_loop_uv_offset);
2845                                 }
2846                         }
2847                 }
2848                 EDBM_uv_vert_map_free(vmap);
2849                 
2850         }
2851         else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
2852                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2853                         if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2854                                 uvedit_face_select_set(scene, em, efa, select, false, cd_loop_uv_offset);
2855                         }
2856                 }
2857         }
2858 }
2859
2860
2861
2862 /**
2863  * Flush the selection from loop tags based on sticky and selection modes.
2864  *
2865  * needed because settings the selection a face is done in a number of places but it also needs to respect
2866  * the sticky modes for the UV verts, so dealing with the sticky modes is best done in a separate function.
2867  *
2868  * \note! This function is very similar to #uv_select_flush_from_tag_loop, be sure to update both upon changing.
2869  */
2870 static void uv_select_flush_from_tag_loop(SpaceImage *sima, Scene *scene, Object *obedit, bool select)
2871 {
2872         /* Selecting UV Loops with some modes requires us to change
2873          * the selection in other faces (depending on the sticky mode).
2874          *
2875          * This only needs to be done when the Mesh is not used for
2876          * selection (so for sticky modes, vertex or location based). */
2877
2878         ToolSettings *ts = scene->toolsettings;
2879         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2880         BMFace *efa;
2881         BMLoop *l;
2882         BMIter iter, liter;
2883         /* MTexPoly *tf; */
2884
2885         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2886
2887
2888         if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
2889                 /* Tag all verts as untouched, then touch the ones that have a face center
2890                  * in the loop and select all MLoopUV's that use a touched vert. */
2891                 BMVert *eve;
2892
2893                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2894                         BM_elem_flag_disable(eve, BM_ELEM_TAG);
2895                 }
2896
2897                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2898                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2899                                 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2900                                         BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2901                                 }
2902                         }
2903                 }
2904
2905                 /* now select tagged verts */
2906                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2907                         /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2908
2909                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2910                                 if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
2911                                         uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2912                                 }
2913                         }
2914                 }
2915         }
2916         else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
2917                 struct UvVertMap *vmap;
2918                 float limit[2];
2919                 unsigned int efa_index;
2920
2921                 uvedit_pixel_to_float(sima, limit, 0.05);
2922
2923                 EDBM_index_arrays_ensure(em, BM_FACE);
2924                 vmap = EDBM_uv_vert_map_create(em, 0, limit);
2925                 if (vmap == NULL) {
2926                         return;
2927                 }
2928
2929                 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
2930                         /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2931
2932                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2933                                 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2934                                         uv_select_flush_from_tag_sticky_loc_internal(scene, em, vmap, efa_index, l,
2935                                                                                      select, cd_loop_uv_offset);
2936                                 }
2937                         }
2938                 }
2939                 EDBM_uv_vert_map_free(vmap);
2940
2941         }
2942         else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
2943                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2944                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2945                                 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2946                                         uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2947                                 }
2948                         }
2949                 }
2950         }
2951 }
2952
2953 /* ******************** border select operator **************** */
2954
2955 static int uv_border_select_exec(bContext *C, wmOperator *op)
2956 {
2957         SpaceImage *sima = CTX_wm_space_image(C);
2958         Scene *scene = CTX_data_scene(C);
2959         ToolSettings *ts = scene->toolsettings;
2960         Object *obedit = CTX_data_edit_object(C);
2961         Image *ima = CTX_data_edit_image(C);
2962         ARegion *ar = CTX_wm_region(C);
2963         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2964         BMFace *efa;
2965         BMLoop *l;
2966         BMIter iter, liter;
2967         MTexPoly *tf;
2968         MLoopUV *luv;
2969         rcti rect;
2970         rctf rectf;
2971         bool change, pinned, select, extend;
2972         const bool use_face_center = (ts->uv_flag & UV_SYNC_SELECTION) ?
2973                                     (ts->selectmode == SCE_SELECT_FACE) :
2974                                     (ts->uv_selectmode == UV_SELECT_FACE);
2975
2976         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2977         const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2978
2979         /* get rectangle from operator */
2980         WM_operator_properties_border_to_rcti(op, &rect);
2981                 
2982         UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
2983         UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
2984
2985         /* figure out what to select/deselect */
2986         select = (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT);
2987         pinned = RNA_boolean_get(op->ptr, "pinned");
2988         extend = RNA_boolean_get(op->ptr, "extend");
2989
2990         if (!extend)
2991                 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
2992
2993         /* do actual selection */
2994         if (use_face_center && !pinned) {
2995                 /* handle face selection mode */
2996                 float cent[2];
2997
2998                 change = false;
2999
3000                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3001                         /* assume not touched */
3002                         BM_elem_flag_disable(efa, BM_ELEM_TAG);
3003
3004                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
3005                         if (uvedit_face_visible_test(scene, ima, efa, tf)) {
3006                                 uv_poly_center(efa, cent, cd_loop_uv_offset);
3007                                 if (BLI_rctf_isect_pt_v(&rectf, cent)) {
3008                                         BM_elem_flag_enable(efa, BM_ELEM_TAG);
3009                                         change = true;
3010                                 }
3011                         }
3012                 }
3013
3014                 /* (de)selects all tagged faces and deals with sticky modes */
3015                 if (change) {
3016                         uv_select_flush_from_tag_face(sima, scene, obedit, select);
3017                 }
3018         }
3019         else {
3020                 /* other selection modes */
3021                 change = true;
3022                 
3023                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3024                         tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
3025                         if (!uvedit_face_visible_test(scene, ima, efa, tf))
3026                                 continue;
3027                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3028                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3029
3030                                 if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION) ) {
3031
3032                                         /* UV_SYNC_SELECTION - can't do pinned selection */
3033                                         if (BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
3034                                                 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
3035                                         }
3036                                 }
3037                                 else if (pinned) {
3038                                         if ((luv->flag & MLOOPUV_PINNED) && BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
3039                                                 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
3040                                         }
3041                                 }
3042                         }
3043