2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): Antony Riakiotakis.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/uvedit/uvedit_ops.c
38 #include "MEM_guardedalloc.h"
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"
50 #include "BLI_lasso.h"
51 #include "BLI_blenlib.h"
52 #include "BLI_array.h"
53 #include "BLI_utildefines.h"
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"
61 #include "BKE_material.h"
64 #include "BKE_report.h"
65 #include "BKE_scene.h"
66 #include "BKE_editmesh.h"
71 #include "ED_uvedit.h"
72 #include "ED_object.h"
73 #include "ED_screen.h"
74 #include "ED_transform.h"
76 #include "RNA_access.h"
77 #include "RNA_define.h"
82 #include "UI_view2d.h"
84 #include "uvedit_intern.h"
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);
90 /************************* state testing ************************/
92 int ED_uvedit_test(Object *obedit)
100 if (obedit->type != OB_MESH)
103 em = BKE_editmesh_from_object(obedit);
104 ret = EDBM_mtexpoly_check(em);
109 static int ED_operator_uvedit_can_uv_sculpt(struct bContext *C)
111 SpaceImage *sima = CTX_wm_space_image(C);
112 ToolSettings *toolsettings = CTX_data_tool_settings(C);
113 Object *obedit = CTX_data_edit_object(C);
115 return ED_space_image_show_uvedit(sima, obedit) && !(toolsettings->use_uv_sculpt);
118 static int UNUSED_FUNCTION(ED_operator_uvmap_mesh) (bContext *C)
120 Object *ob = CTX_data_active_object(C);
122 if (ob && ob->type == OB_MESH) {
125 if (CustomData_get_layer(&me->fdata, CD_MTFACE) != NULL)
131 /**************************** object active image *****************************/
133 static int is_image_texture_node(bNode *node)
135 return ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT);
138 int ED_object_get_active_image(Object *ob, int mat_nr, Image **ima, ImageUser **iuser, bNode **node_r)
140 Material *ma = give_current_material(ob, mat_nr);
141 bNode *node = (ma && ma->use_nodes) ? nodeGetActiveTexture(ma->nodetree) : NULL;
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;
150 if (ima) *ima = NULL;
151 if (iuser) *iuser = NULL;
152 if (node_r) *node_r = node;
157 void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima)
159 Material *ma = give_current_material(ob, mat_nr);
160 bNode *node = (ma && ma->use_nodes) ? nodeGetActiveTexture(ma->nodetree) : NULL;
162 if (node && is_image_texture_node(node)) {
164 ED_node_tag_update_nodetree(bmain, ma->nodetree);
168 /************************* assign image ************************/
170 //#define USE_SWITCH_ASPECT
172 void ED_uvedit_assign_image(Main *UNUSED(bmain), Scene *scene, Object *obedit, Image *ima, Image *previma)
178 const bool selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
180 /* skip assigning these procedural images... */
181 if (ima && (ima->type == IMA_TYPE_R_RESULT || ima->type == IMA_TYPE_COMPOSITE))
184 /* verify we have a mesh we can work with */
185 if (!obedit || (obedit->type != OB_MESH))
188 em = BKE_editmesh_from_object(obedit);
189 if (!em || !em->bm->totface) {
193 if (BKE_scene_use_new_shading_nodes(scene)) {
194 /* new shading system, do not assign anything */
199 int cd_loop_uv_offset;
200 int cd_poly_tex_offset;
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;
207 ED_image_get_uv_aspect(previma, prev_aspect, prev_aspect + 1);
208 ED_image_get_uv_aspect(ima, aspect, aspect + 1);
210 fprev_aspect = prev_aspect[0] / prev_aspect[1];
211 faspect = aspect[0] / aspect[1];
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));
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);
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);
230 if (uvedit_face_visible_test(scene, previma, efa, tf) &&
231 (selected == true || uvedit_face_select_test(scene, efa, cd_loop_uv_offset)))
236 if (ima->id.us == 0) id_us_plus(&ima->id);
237 else id_lib_extern(&ima->id);
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) {
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);
251 luv->uv[0] *= fprev_aspect;
252 luv->uv[0] /= faspect;
265 /* and update depdency graph */
267 DAG_id_tag_update(obedit->data, 0);
273 /* dotile - 1, set the tile flag (from the space image)
274 * 2, set the tile index for the faces. */
275 static bool uvedit_set_tile(Object *obedit, Image *ima, int curtile)
281 int cd_poly_tex_offset;
283 /* verify if we have something to do */
284 if (!ima || !ED_uvedit_test(obedit))
287 if ((ima->tpageflag & IMA_TILES) == 0)
290 /* skip assigning these procedural images... */
291 if (ima->type == IMA_TYPE_R_RESULT || ima->type == IMA_TYPE_COMPOSITE)
294 em = BKE_editmesh_from_object(obedit);
296 cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
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);
301 if (BM_elem_flag_test(efa, BM_ELEM_SELECT))
302 tf->tile = curtile; /* set tile index */
305 DAG_id_tag_update(obedit->data, 0);
310 /*********************** space conversion *********************/
312 static void uvedit_pixel_to_float(SpaceImage *sima, float *dist, float pixeldist)
317 ED_space_image_get_size(sima, &width, &height);
320 width = IMG_SIZE_FALLBACK;
321 height = IMG_SIZE_FALLBACK;
324 dist[0] = pixeldist / width;
325 dist[1] = pixeldist / height;
328 /*************** visibility and selection utilities **************/
330 bool uvedit_face_visible_nolocal(Scene *scene, BMFace *efa)
332 ToolSettings *ts = scene->toolsettings;
334 if (ts->uv_flag & UV_SYNC_SELECTION)
335 return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0);
337 return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT));
340 bool uvedit_face_visible_test(Scene *scene, Image *ima, BMFace *efa, MTexPoly *tf)
342 ToolSettings *ts = scene->toolsettings;
344 if (ts->uv_flag & UV_SHOW_SAME_IMAGE)
345 return (tf->tpage == ima) ? uvedit_face_visible_nolocal(scene, efa) : false;
347 return uvedit_face_visible_nolocal(scene, efa);
350 bool uvedit_face_select_test(Scene *scene, BMFace *efa,
351 const int cd_loop_uv_offset)
353 ToolSettings *ts = scene->toolsettings;
354 if (ts->uv_flag & UV_SYNC_SELECTION) {
355 return (BM_elem_flag_test(efa, BM_ELEM_SELECT));
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))
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)
376 return uvedit_face_select_enable(scene, em, efa, do_history, cd_loop_uv_offset);
379 return uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
383 bool uvedit_face_select_enable(Scene *scene, BMEditMesh *em, BMFace *efa, const bool do_history,
384 const int cd_loop_uv_offset)
386 ToolSettings *ts = scene->toolsettings;
388 if (ts->uv_flag & UV_SYNC_SELECTION) {
389 BM_face_select_set(em->bm, efa, TRUE);
391 BM_select_history_store(em->bm, (BMElem *)efa);
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;
410 bool uvedit_face_select_disable(Scene *scene, BMEditMesh *em, BMFace *efa,
411 const int cd_loop_uv_offset)
413 ToolSettings *ts = scene->toolsettings;
415 if (ts->uv_flag & UV_SYNC_SELECTION) {
416 BM_face_select_set(em->bm, efa, FALSE);
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;
434 bool uvedit_edge_select_test(Scene *scene, BMLoop *l,
435 const int cd_loop_uv_offset)
437 ToolSettings *ts = scene->toolsettings;
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);
443 else if (ts->selectmode == SCE_SELECT_EDGE) {
444 return BM_elem_flag_test(l->e, BM_ELEM_SELECT);
447 return BM_elem_flag_test(l->v, BM_ELEM_SELECT) &&
448 BM_elem_flag_test(l->next->v, BM_ELEM_SELECT);
452 MLoopUV *luv1, *luv2;
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);
457 return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL);
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)
466 uvedit_edge_select_enable(em, scene, l, do_history, cd_loop_uv_offset);
469 uvedit_edge_select_disable(em, scene, l, cd_loop_uv_offset);
473 void uvedit_edge_select_enable(BMEditMesh *em, Scene *scene, BMLoop *l, const bool do_history,
474 const int cd_loop_uv_offset)
477 ToolSettings *ts = scene->toolsettings;
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);
485 BM_vert_select_set(em->bm, l->e->v1, TRUE);
486 BM_vert_select_set(em->bm, l->e->v2, TRUE);
490 BM_select_history_store(em->bm, (BMElem *)l->e);
494 MLoopUV *luv1, *luv2;
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);
499 luv1->flag |= MLOOPUV_VERTSEL;
500 luv2->flag |= MLOOPUV_VERTSEL;
504 void uvedit_edge_select_disable(BMEditMesh *em, Scene *scene, BMLoop *l,
505 const int cd_loop_uv_offset)
508 ToolSettings *ts = scene->toolsettings;
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);
516 BM_vert_select_set(em->bm, l->e->v1, FALSE);
517 BM_vert_select_set(em->bm, l->e->v2, FALSE);
521 MLoopUV *luv1, *luv2;
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);
526 luv1->flag &= ~MLOOPUV_VERTSEL;
527 luv2->flag &= ~MLOOPUV_VERTSEL;
531 bool uvedit_uv_select_test(Scene *scene, BMLoop *l,
532 const int cd_loop_uv_offset)
534 ToolSettings *ts = scene->toolsettings;
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);
540 return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT);
543 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
544 return (luv->flag & MLOOPUV_VERTSEL) != 0;
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)
552 uvedit_uv_select_enable(em, scene, l, do_history, cd_loop_uv_offset);
555 uvedit_uv_select_disable(em, scene, l, cd_loop_uv_offset);
559 void uvedit_uv_select_enable(BMEditMesh *em, Scene *scene, BMLoop *l,
560 const bool do_history, const int cd_loop_uv_offset)
562 ToolSettings *ts = scene->toolsettings;
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);
568 BM_vert_select_set(em->bm, l->v, TRUE);
571 BM_select_history_remove(em->bm, (BMElem *)l->v);
575 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
576 luv->flag |= MLOOPUV_VERTSEL;
580 void uvedit_uv_select_disable(BMEditMesh *em, Scene *scene, BMLoop *l,
581 const int cd_loop_uv_offset)
583 ToolSettings *ts = scene->toolsettings;
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);
589 BM_vert_select_set(em->bm, l->v, FALSE);
592 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
593 luv->flag &= ~MLOOPUV_VERTSEL;
597 /*********************** live unwrap utilities ***********************/
599 void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
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);
608 /*********************** geometric utilities ***********************/
609 void uv_poly_center(BMFace *f, float r_cent[2], const int cd_loop_uv_offset)
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);
622 mul_v2_fl(r_cent, 1.0f / (float)f->len);
625 void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len)
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;
634 bool ED_uvedit_minmax(Scene *scene, Image *ima, Object *obedit, float r_min[2], float r_max[2])
636 BMEditMesh *em = BKE_editmesh_from_object(obedit);
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);
647 INIT_MINMAX2(r_min, r_max);
649 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
650 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
651 if (!uvedit_face_visible_test(scene, ima, efa, tf))
654 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
655 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
656 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
657 minmax_v2v2_v2(r_min, r_max, luv->uv);
666 static bool ED_uvedit_median(Scene *scene, Image *ima, Object *obedit, float co[2])
668 BMEditMesh *em = BKE_editmesh_from_object(obedit);
674 unsigned int sel = 0;
676 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
677 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
680 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
681 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
682 if (!uvedit_face_visible_test(scene, ima, efa, tf))
685 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
686 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
687 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
688 add_v2_v2(co, luv->uv);
694 mul_v2_fl(co, 1.0f / (float)sel);
699 static bool uvedit_center(Scene *scene, Image *ima, Object *obedit, float cent[2], char mode)
703 if (mode == V3D_CENTER) { /* bounding box */
704 float min[2], max[2];
705 if (ED_uvedit_minmax(scene, ima, obedit, min, max)) {
706 mid_v2_v2v2(cent, min, max);
711 if (ED_uvedit_median(scene, ima, obedit, cent)) {
719 /************************** find nearest ****************************/
721 void uv_find_nearest_edge(Scene *scene, Image *ima, BMEditMesh *em, const float co[2], NearestHit *hit)
727 MLoopUV *luv, *luv_next;
728 float mindist_squared, dist_squared;
731 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
732 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
734 mindist_squared = 1e10f;
735 memset(hit, 0, sizeof(*hit));
737 BM_mesh_elem_index_ensure(em->bm, BM_VERT);
739 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
740 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
741 if (!uvedit_face_visible_test(scene, ima, efa, tf))
745 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
746 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
747 luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
749 dist_squared = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
751 if (dist_squared < mindist_squared) {
756 hit->nextl = l->next;
758 hit->luv_next = luv_next;
760 hit->vert1 = BM_elem_index_get(hit->l->v);
761 hit->vert2 = BM_elem_index_get(hit->l->next->v);
763 mindist_squared = dist_squared;
771 static void uv_find_nearest_face(Scene *scene, Image *ima, BMEditMesh *em, const float co[2], NearestHit *hit)
776 float mindist, dist, cent[2];
778 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
779 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
782 memset(hit, 0, sizeof(*hit));
784 /*this will fill in hit.vert1 and hit.vert2*/
785 uv_find_nearest_edge(scene, ima, em, co, hit);
786 hit->l = hit->nextl = NULL;
787 hit->luv = hit->luv_next = NULL;
789 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
790 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
791 if (!uvedit_face_visible_test(scene, ima, efa, tf))
794 uv_poly_center(efa, cent, cd_loop_uv_offset);
796 dist = fabsf(co[0] - cent[0]) + fabsf(co[1] - cent[1]);
798 if (dist < mindist) {
806 static int uv_nearest_between(BMEditMesh *em, BMFace *efa, int UNUSED(nverts), int id,
807 const float co[2], const float uv[2])
812 float m[2], v1[2], v2[2], c1, c2, *uv1 = NULL, /* *uv2, */ /* UNUSED */ *uv3 = NULL;
814 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
816 id1 = (id + efa->len - 1) % efa->len;
817 id2 = (id + efa->len + 1) % efa->len;
819 sub_v2_v2v2(m, co, uv);
822 BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
823 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
829 /* uv2 = luv->uv; */ /* UNUSED */
837 sub_v2_v2v2(v1, uv1, uv);
838 sub_v2_v2v2(v2, uv3, uv);
840 /* m and v2 on same side of v-v1? */
841 c1 = v1[0] * m[1] - v1[1] * m[0];
842 c2 = v1[0] * v2[1] - v1[1] * v2[0];
847 /* m and v1 on same side of v-v2? */
848 c1 = v2[0] * m[1] - v2[1] * m[0];
849 c2 = v2[0] * v1[1] - v2[1] * v1[0];
851 return (c1 * c2 >= 0.0f);
854 void uv_find_nearest_vert(Scene *scene, Image *ima, BMEditMesh *em,
855 float const co[2], const float penalty[2], NearestHit *hit)
865 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
866 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
868 /*this will fill in hit.vert1 and hit.vert2*/
869 uv_find_nearest_edge(scene, ima, em, co, hit);
870 hit->l = hit->nextl = NULL;
871 hit->luv = hit->luv_next = NULL;
874 memset(hit, 0, sizeof(*hit));
876 BM_mesh_elem_index_ensure(em->bm, BM_VERT);
878 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
879 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
880 if (!uvedit_face_visible_test(scene, ima, efa, tf))
884 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
885 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
887 if (penalty && uvedit_uv_select_test(scene, l, cd_loop_uv_offset))
888 dist = fabsf(co[0] - luv->uv[0]) + penalty[0] + fabsf(co[1] - luv->uv[1]) + penalty[1];
890 dist = fabsf(co[0] - luv->uv[0]) + fabsf(co[1] - luv->uv[1]);
892 if (dist <= mindist) {
894 if (!uv_nearest_between(em, efa, efa->len, i, co, luv->uv)) {
902 hit->nextl = l->next;
904 hit->luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
908 hit->vert1 = BM_elem_index_get(hit->l->v);
916 int ED_uvedit_nearest_uv(Scene *scene, Object *obedit, Image *ima, const float co[2], float r_uv[2])
918 BMEditMesh *em = BKE_editmesh_from_object(obedit);
927 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
928 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
931 copy_v2_v2(r_uv, co);
933 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
934 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
935 if (!uvedit_face_visible_test(scene, ima, efa, tf))
938 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
939 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
940 dist = fabsf(co[0] - luv->uv[0]) + fabsf(co[1] - luv->uv[1]);
942 if (dist <= mindist) {
945 copy_v2_v2(r_uv, luv->uv);
954 /*********************** loop select ***********************/
956 static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first)
961 for (iterv = first; iterv; iterv = iterv->next) {
962 if (iterv->separate && iterv != first)
972 static UvMapVert *uv_select_edgeloop_vertex_map_get(UvVertMap *vmap, BMFace *efa, int a)
974 UvMapVert *iterv, *first;
977 l = BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, a);
978 first = EDBM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
980 for (iterv = first; iterv; iterv = iterv->next) {
983 if (iterv->f == BM_elem_index_get(efa))
990 static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, UvMapVert *first1, UvMapVert *first2, int *totface)
992 UvMapVert *iterv1, *iterv2;
996 /* count number of faces this edge has */
997 for (iterv1 = first1; iterv1; iterv1 = iterv1->next) {
998 if (iterv1->separate && iterv1 != first1)
1001 for (iterv2 = first2; iterv2; iterv2 = iterv2->next) {
1002 if (iterv2->separate && iterv2 != first2)
1005 if (iterv1->f == iterv2->f) {
1006 /* if face already tagged, don't do this edge */
1007 efa = EDBM_face_at_index(em, iterv1->f);
1008 if (BM_elem_flag_test(efa, BM_ELEM_TAG))
1017 if (*totface == 0) /* start edge */
1019 else if (tot != *totface) /* check for same number of faces as start edge */
1023 for (iterv1 = first1; iterv1; iterv1 = iterv1->next) {
1024 if (iterv1->separate && iterv1 != first1)
1027 for (iterv2 = first2; iterv2; iterv2 = iterv2->next) {
1028 if (iterv2->separate && iterv2 != first2)
1031 if (iterv1->f == iterv2->f) {
1032 efa = EDBM_face_at_index(em, iterv1->f);
1033 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1042 static int uv_select_edgeloop(Scene *scene, Image *ima, BMEditMesh *em, NearestHit *hit,
1043 float limit[2], const bool extend)
1050 UvMapVert *iterv1, *iterv2;
1051 int a, looking, nverts, starttotf, select;
1053 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1054 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1057 EDBM_index_arrays_ensure(em, BM_FACE);
1058 vmap = EDBM_uv_vert_map_create(em, 0, limit);
1060 BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE);
1063 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
1066 BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, FALSE);
1068 /* set flags for first face and verts */
1069 nverts = hit->efa->len;
1070 iterv1 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->lindex);
1071 iterv2 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, (hit->lindex + 1) % nverts);
1072 uv_select_edgeloop_vertex_loop_flag(iterv1);
1073 uv_select_edgeloop_vertex_loop_flag(iterv2);
1076 uv_select_edgeloop_edge_tag_faces(em, iterv1, iterv2, &starttotf);
1078 /* sorry, first edge isn't even ok */
1079 if (iterv1->flag == 0 && iterv2->flag == 0) looking = 0;
1086 /* find correct valence edges which are not tagged yet, but connect to tagged one */
1088 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1089 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1091 if (!BM_elem_flag_test(efa, BM_ELEM_TAG) && uvedit_face_visible_test(scene, ima, efa, tf)) {
1093 for (a = 0; a < nverts; a++) {
1094 /* check face not hidden and not tagged */
1095 iterv1 = uv_select_edgeloop_vertex_map_get(vmap, efa, a);
1096 iterv2 = uv_select_edgeloop_vertex_map_get(vmap, efa, (a + 1) % nverts);
1098 if (!iterv1 || !iterv2)
1101 /* check if vertex is tagged and has right valence */
1102 if (iterv1->flag || iterv2->flag) {
1103 if (uv_select_edgeloop_edge_tag_faces(em, iterv1, iterv2, &starttotf)) {
1105 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1107 uv_select_edgeloop_vertex_loop_flag(iterv1);
1108 uv_select_edgeloop_vertex_loop_flag(iterv2);
1117 /* do the actual select/deselect */
1118 nverts = hit->efa->len;
1119 iterv1 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->lindex);
1120 iterv2 = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, (hit->lindex + 1) % nverts);
1125 if (uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset))
1133 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1135 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1136 iterv1 = uv_select_edgeloop_vertex_map_get(vmap, efa, a);
1139 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
1147 EDBM_uv_vert_map_free(vmap);
1149 return (select) ? 1 : -1;
1152 /*********************** linked select ***********************/
1154 static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const float limit[2], NearestHit *hit, bool extend)
1162 UvMapVert *vlist, *iterv, *startv;
1163 int i, stacksize = 0, *stack;
1167 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1168 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1170 EDBM_index_arrays_ensure(em, BM_FACE); /* we can use this too */
1171 vmap = EDBM_uv_vert_map_create(em, 1, limit);
1176 stack = MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack");
1177 flag = MEM_callocN(sizeof(*flag) * em->bm->totface, "UvLinkFlag");
1180 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1181 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1183 if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1184 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1185 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1187 if (luv->flag & MLOOPUV_VERTSEL) {
1188 stack[stacksize] = a;
1200 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1201 if (efa == hit->efa) {
1202 stack[stacksize] = a;
1212 while (stacksize > 0) {
1216 a = stack[stacksize];
1219 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1227 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1229 /* make_uv_vert_map_EM sets verts tmp.l to the indices */
1230 vlist = EDBM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
1234 for (iterv = vlist; iterv; iterv = iterv->next) {
1235 if (iterv->separate)
1241 for (iterv = startv; iterv; iterv = iterv->next) {
1242 if ((startv != iterv) && (iterv->separate))
1244 else if (!flag[iterv->f]) {
1246 stack[stacksize] = iterv->f;
1257 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1258 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1259 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1262 luv->flag |= MLOOPUV_VERTSEL;
1264 luv->flag &= ~MLOOPUV_VERTSEL;
1271 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1277 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1278 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1280 if (luv->flag & MLOOPUV_VERTSEL)
1292 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1298 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1299 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1301 luv->flag &= ~MLOOPUV_VERTSEL;
1309 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1315 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1316 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1318 luv->flag |= MLOOPUV_VERTSEL;
1328 EDBM_uv_vert_map_free(vmap);
1331 /* WATCH IT: this returns first selected UV,
1332 * not ideal in many cases since there could be multiple */
1333 static float *uv_sel_co_from_eve(Scene *scene, Image *ima, BMEditMesh *em, BMVert *eve)
1338 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1339 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1341 BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
1342 MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1344 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1347 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1348 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1356 static int uv_select_more_less(bContext *C, const bool select)
1358 Scene *scene = CTX_data_scene(C);
1359 Object *obedit = CTX_data_edit_object(C);
1360 Image *ima = CTX_data_edit_image(C);
1361 SpaceImage *sima = CTX_wm_space_image(C);
1362 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1367 ToolSettings *ts = scene->toolsettings;
1369 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1370 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1372 if (ts->uv_flag & UV_SYNC_SELECTION) {
1374 EDBM_select_more(em);
1377 EDBM_select_less(em);
1380 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1381 return OPERATOR_FINISHED;
1384 if (ts->uv_selectmode == UV_SELECT_FACE) {
1387 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1388 BM_elem_flag_disable(efa, BM_ELEM_TAG);
1391 /* mark loops to be selected */
1392 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1393 MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1395 if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1402 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1403 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1404 if (luv->flag & MLOOPUV_VERTSEL) {
1405 sel_state |= IS_SEL;
1408 sel_state |= IS_UNSEL;
1411 /* if we have a mixed selection, tag to grow it */
1412 if (sel_state == (IS_SEL | IS_UNSEL)) {
1413 BM_elem_flag_enable(efa, BM_ELEM_TAG);
1424 /* select tagged faces */
1425 uv_select_flush_from_tag_face(sima, scene, obedit, select);
1430 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1431 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1432 BM_elem_flag_disable(l, BM_ELEM_TAG);
1436 /* mark loops to be selected */
1437 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1438 MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1440 if (uvedit_face_visible_test(scene, ima, efa, tf)) {
1441 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1443 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1445 if (((luv->flag & MLOOPUV_VERTSEL) != 0) == select) {
1446 BM_elem_flag_enable(l->next, BM_ELEM_TAG);
1447 BM_elem_flag_enable(l->prev, BM_ELEM_TAG);
1453 /* select tagged loops */
1454 uv_select_flush_from_tag_loop(sima, scene, obedit, select);
1457 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1459 return OPERATOR_FINISHED;
1462 static int uv_select_more_exec(bContext *C, wmOperator *UNUSED(op))
1464 return uv_select_more_less(C, true);
1467 static void UV_OT_select_more(wmOperatorType *ot)
1470 ot->name = "Select More";
1471 ot->description = "Select more UV vertices connected to initial selection";
1472 ot->idname = "UV_OT_select_more";
1473 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1476 ot->exec = uv_select_more_exec;
1477 ot->poll = ED_operator_uvedit_space_image;
1480 static int uv_select_less_exec(bContext *C, wmOperator *UNUSED(op))
1482 return uv_select_more_less(C, false);
1485 static void UV_OT_select_less(wmOperatorType *ot)
1488 ot->name = "Select Less";
1489 ot->description = "Deselect UV vertices at the boundary of each selection region";
1490 ot->idname = "UV_OT_select_less";
1491 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1494 ot->exec = uv_select_less_exec;
1495 ot->poll = ED_operator_uvedit_space_image;
1498 /* ******************** align operator **************** */
1500 static void uv_weld_align(bContext *C, int tool)
1502 Object *obedit = CTX_data_edit_object(C);
1503 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1508 float cent[2], min[2], max[2];
1510 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1511 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1513 scene = CTX_data_scene(C);
1514 ima = CTX_data_edit_image(C);
1515 sima = CTX_wm_space_image(C);
1517 INIT_MINMAX2(min, max);
1524 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1525 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1527 if (!uvedit_face_visible_test(scene, ima, efa, tf))
1530 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1531 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1532 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1533 minmax_v2v2_v2(min, max, luv->uv);
1538 tool = (max[0] - min[0] >= max[1] - min[1]) ? 'y' : 'x';
1541 uvedit_center(scene, ima, obedit, cent, 0);
1543 if (tool == 'x' || tool == 'w') {
1548 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1549 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1550 if (!uvedit_face_visible_test(scene, ima, efa, tf))
1553 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1554 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1555 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1556 luv->uv[0] = cent[0];
1563 if (tool == 'y' || tool == 'w') {
1568 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1569 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1570 if (!uvedit_face_visible_test(scene, ima, efa, tf))
1573 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1574 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1575 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1576 luv->uv[1] = cent[1];
1583 if (tool == 's' || tool == 't' || tool == 'u') {
1588 BMIter iter, liter, eiter;
1591 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1592 BM_elem_flag_disable(eve, BM_ELEM_TAG);
1595 /* tag verts with a selected UV */
1596 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1597 BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
1598 tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1600 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1603 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1604 BM_elem_flag_enable(eve, BM_ELEM_TAG);
1610 /* flush vertex tags to edges */
1611 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1612 BM_elem_flag_set(eed, BM_ELEM_TAG, (BM_elem_flag_test(eed->v1, BM_ELEM_TAG) &&
1613 BM_elem_flag_test(eed->v2, BM_ELEM_TAG)));
1616 /* find a vertex with only one tagged edge */
1618 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1619 int tot_eed_tag = 0;
1620 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
1621 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
1626 if (tot_eed_tag == 1) {
1633 BMVert **eve_line = NULL;
1634 BMVert *eve_next = NULL;
1635 BLI_array_declare(eve_line);
1640 /* walk over edges, building an array of verts in a line */
1642 BLI_array_append(eve_line, eve);
1643 /* don't touch again */
1644 BM_elem_flag_disable(eve, BM_ELEM_TAG);
1649 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
1650 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
1651 BMVert *eve_other = BM_edge_other_vert(eed, eve);
1652 if (BM_elem_flag_test(eve_other, BM_ELEM_TAG)) {
1653 /* this is a tagged vert we didnt walk over yet, step onto it */
1654 eve_next = eve_other;
1663 /* now we have all verts, make into a line */
1664 if (BLI_array_count(eve_line) > 2) {
1666 /* we know the returns from these must be valid */
1667 float *uv_start = uv_sel_co_from_eve(scene, ima, em, eve_line[0]);
1668 float *uv_end = uv_sel_co_from_eve(scene, ima, em, eve_line[BLI_array_count(eve_line) - 1]);
1669 /* For t & u modes */
1673 if (uv_start[1] == uv_end[1])
1676 a = (uv_end[0] - uv_start[0]) / (uv_end[1] - uv_start[1]);
1678 else if (tool == 'u') {
1679 if (uv_start[0] == uv_end[0])
1682 a = (uv_end[1] - uv_start[1]) / (uv_end[0] - uv_start[0]);
1685 /* go over all verts except for endpoints */
1686 for (i = 0; i < BLI_array_count(eve_line); i++) {
1687 BM_ITER_ELEM (l, &liter, eve_line[i], BM_LOOPS_OF_VERT) {
1688 tf = BM_ELEM_CD_GET_VOID_P(l->f, cd_poly_tex_offset);
1690 if (!uvedit_face_visible_test(scene, ima, l->f, tf))
1693 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1694 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1695 /* Projection of point (x, y) over line (x1, y1, x2, y2) along X axis:
1696 * new_y = (y2 - y1) / (x2 - x1) * (x - x1) + y1
1697 * Maybe this should be a BLI func? Or is it already existing?
1698 * Could use interp_v2_v2v2, but not sure it's worth it here...*/
1700 luv->uv[0] = a * (luv->uv[1] - uv_start[1]) + uv_start[0];
1701 else if (tool == 'u')
1702 luv->uv[1] = a * (luv->uv[0] - uv_start[0]) + uv_start[1];
1704 closest_to_line_segment_v2(luv->uv, luv->uv, uv_start, uv_end);
1710 /* error - not a line, needs 3+ points */
1714 MEM_freeN(eve_line);
1718 /* error - cant find an endpoint */
1723 uvedit_live_unwrap_update(sima, scene, obedit);
1724 DAG_id_tag_update(obedit->data, 0);
1725 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1728 static int uv_align_exec(bContext *C, wmOperator *op)
1730 uv_weld_align(C, RNA_enum_get(op->ptr, "axis"));
1732 return OPERATOR_FINISHED;
1735 static void UV_OT_align(wmOperatorType *ot)
1737 static EnumPropertyItem axis_items[] = {
1738 {'s', "ALIGN_S", 0, "Straighten", "Align UVs along the line defined by the endpoints"},
1739 {'t', "ALIGN_T", 0, "Straighten X", "Align UVs along the line defined by the endpoints along the X axis"},
1740 {'u', "ALIGN_U", 0, "Straighten Y", "Align UVs along the line defined by the endpoints along the Y axis"},
1741 {'a', "ALIGN_AUTO", 0, "Align Auto", "Automatically choose the axis on which there is most alignment already"},
1742 {'x', "ALIGN_X", 0, "Align X", "Align UVs on X axis"},
1743 {'y', "ALIGN_Y", 0, "Align Y", "Align UVs on Y axis"},
1744 {0, NULL, 0, NULL, NULL}};
1748 ot->description = "Align selected UV vertices to an axis";
1749 ot->idname = "UV_OT_align";
1750 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1753 ot->exec = uv_align_exec;
1754 ot->poll = ED_operator_uvedit;
1757 RNA_def_enum(ot->srna, "axis", axis_items, 'a', "Axis", "Axis to align UV locations on");
1759 /* ******************** weld near operator **************** */
1761 typedef struct UVvert {
1766 static int uv_remove_doubles_exec(bContext *C, wmOperator *op)
1768 const float threshold = RNA_float_get(op->ptr, "threshold");
1769 const int use_unselected = RNA_boolean_get(op->ptr, "use_unselected");
1773 Object *obedit = CTX_data_edit_object(C);
1774 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1786 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1787 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1789 sima = CTX_wm_space_image(C);
1790 scene = CTX_data_scene(C);
1791 ima = CTX_data_edit_image(C);
1793 if (use_unselected == FALSE) {
1794 UVvert *vert_arr = NULL;
1795 BLI_array_declare(vert_arr);
1796 MLoopUV **loop_arr = NULL;
1797 BLI_array_declare(loop_arr);
1799 /* TODO, use qsort as with MESH_OT_remove_doubles, this isn't optimal */
1800 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1801 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1802 if (!uvedit_face_visible_test(scene, ima, efa, tf))
1805 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1806 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1807 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1811 BLI_array_append(vert_arr, vert);
1817 for (uv_a_index = 0; uv_a_index < BLI_array_count(vert_arr); uv_a_index++) {
1818 if (vert_arr[uv_a_index].weld == FALSE) {
1822 BLI_array_empty(loop_arr);
1823 BLI_array_append(loop_arr, vert_arr[uv_a_index].uv_loop);
1825 uv_a = vert_arr[uv_a_index].uv_loop->uv;
1827 copy_v2_v2(uv_max, uv_a);
1828 copy_v2_v2(uv_min, uv_a);
1830 vert_arr[uv_a_index].weld = TRUE;
1831 for (uv_b_index = uv_a_index + 1; uv_b_index < BLI_array_count(vert_arr); uv_b_index++) {
1832 uv_b = vert_arr[uv_b_index].uv_loop->uv;
1833 if ((vert_arr[uv_b_index].weld == FALSE) &&
1834 (len_manhattan_v2v2(uv_a, uv_b) < threshold))
1836 minmax_v2v2_v2(uv_max, uv_min, uv_b);
1837 BLI_array_append(loop_arr, vert_arr[uv_b_index].uv_loop);
1838 vert_arr[uv_b_index].weld = TRUE;
1841 if (BLI_array_count(loop_arr)) {
1843 mid_v2_v2v2(uv_mid, uv_min, uv_max);
1844 for (uv_b_index = 0; uv_b_index < BLI_array_count(loop_arr); uv_b_index++) {
1845 copy_v2_v2(loop_arr[uv_b_index]->uv, uv_mid);
1851 BLI_array_free(vert_arr);
1852 BLI_array_free(loop_arr);
1855 /* selected -> unselected
1857 * No need to use 'UVvert' here */
1858 MLoopUV **loop_arr = NULL;
1859 BLI_array_declare(loop_arr);
1860 MLoopUV **loop_arr_unselected = NULL;
1861 BLI_array_declare(loop_arr_unselected);
1863 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1864 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1865 if (!uvedit_face_visible_test(scene, ima, efa, tf))
1868 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1869 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1870 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1871 BLI_array_append(loop_arr, luv);
1874 BLI_array_append(loop_arr_unselected, luv);
1879 for (uv_a_index = 0; uv_a_index < BLI_array_count(loop_arr); uv_a_index++) {
1880 float dist_best = FLT_MAX, dist;
1881 float *uv_best = NULL;
1883 uv_a = loop_arr[uv_a_index]->uv;
1884 for (uv_b_index = 0; uv_b_index < BLI_array_count(loop_arr_unselected); uv_b_index++) {
1885 uv_b = loop_arr_unselected[uv_b_index]->uv;
1886 dist = len_manhattan_v2v2(uv_a, uv_b);
1887 if ((dist < threshold) && (dist < dist_best)) {
1893 copy_v2_v2(uv_a, uv_best);
1897 BLI_array_free(loop_arr);
1898 BLI_array_free(loop_arr_unselected);
1901 uvedit_live_unwrap_update(sima, scene, obedit);
1902 DAG_id_tag_update(obedit->data, 0);
1903 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1905 return OPERATOR_FINISHED;
1908 static void UV_OT_remove_doubles(wmOperatorType *ot)
1911 ot->name = "Remove Doubles UV";
1912 ot->description = "Selected UV vertices that are within a radius of each other are welded together";
1913 ot->idname = "UV_OT_remove_doubles";
1914 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1917 ot->exec = uv_remove_doubles_exec;
1918 ot->poll = ED_operator_uvedit;
1920 RNA_def_float(ot->srna, "threshold", 0.02f, 0.0f, 10.0f,
1921 "Merge Distance", "Maximum distance between welded vertices", 0.0f, 1.0f);
1922 RNA_def_boolean(ot->srna, "use_unselected", 0, "Unselected", "Merge selected to other unselected vertices");
1924 /* ******************** weld operator **************** */
1926 static int uv_weld_exec(bContext *C, wmOperator *UNUSED(op))
1928 uv_weld_align(C, 'w');
1930 return OPERATOR_FINISHED;
1933 static void UV_OT_weld(wmOperatorType *ot)
1937 ot->description = "Weld selected UV vertices together";
1938 ot->idname = "UV_OT_weld";
1939 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1942 ot->exec = uv_weld_exec;
1943 ot->poll = ED_operator_uvedit;
1947 /* ******************** (de)select all operator **************** */
1949 static void uv_select_all_perform(Scene *scene, Image *ima, BMEditMesh *em, int action)
1951 ToolSettings *ts = scene->toolsettings;
1958 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1959 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
1961 if (ts->uv_flag & UV_SYNC_SELECTION) {
1965 EDBM_select_toggle_all(em);
1968 EDBM_flag_enable_all(em, BM_ELEM_SELECT);
1971 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1974 EDBM_select_swap(em);
1975 EDBM_selectmode_flush(em);
1980 if (action == SEL_TOGGLE) {
1981 action = SEL_SELECT;
1982 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1983 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
1985 if (!uvedit_face_visible_test(scene, ima, efa, tf))
1988 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1989 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1991 if (luv->flag & MLOOPUV_VERTSEL) {
1992 action = SEL_DESELECT;
2000 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2001 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2003 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2006 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2007 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2011 luv->flag |= MLOOPUV_VERTSEL;
2014 luv->flag &= ~MLOOPUV_VERTSEL;
2017 luv->flag ^= MLOOPUV_VERTSEL;
2025 static int uv_select_all_exec(bContext *C, wmOperator *op)
2027 Scene *scene = CTX_data_scene(C);
2028 Object *obedit = CTX_data_edit_object(C);
2029 Image *ima = CTX_data_edit_image(C);
2030 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2032 int action = RNA_enum_get(op->ptr, "action");
2034 uv_select_all_perform(scene, ima, em, action);
2036 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2038 return OPERATOR_FINISHED;
2041 static void UV_OT_select_all(wmOperatorType *ot)
2044 ot->name = "(De)select All";
2045 ot->description = "Change selection of all UV vertices";
2046 ot->idname = "UV_OT_select_all";
2047 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2050 ot->exec = uv_select_all_exec;
2051 ot->poll = ED_operator_uvedit;
2053 WM_operator_properties_select_all(ot);
2056 /* ******************** mouse select operator **************** */
2058 static bool uv_sticky_select(float *limit, int hitv[4], int v, float *hituv[4], float *uv, int sticky, int hitlen)
2062 /* this function test if some vertex needs to selected
2063 * in addition to the existing ones due to sticky select */
2064 if (sticky == SI_STICKY_DISABLE)
2067 for (i = 0; i < hitlen; i++) {
2069 if (sticky == SI_STICKY_LOC) {
2070 if (fabsf(hituv[i][0] - uv[0]) < limit[0] && fabsf(hituv[i][1] - uv[1]) < limit[1])
2073 else if (sticky == SI_STICKY_VERTEX)
2081 static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loop)
2083 SpaceImage *sima = CTX_wm_space_image(C);
2084 Scene *scene = CTX_data_scene(C);
2085 ToolSettings *ts = scene->toolsettings;
2086 Object *obedit = CTX_data_edit_object(C);
2087 Image *ima = CTX_data_edit_image(C);
2088 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2095 int i, selectmode, sticky, sync, *hitv = NULL;
2097 BLI_array_declare(hitv);
2098 int flush = 0, hitlen = 0; /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */
2099 float limit[2], **hituv = NULL;
2100 BLI_array_declare(hituv);
2103 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2104 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2106 /* notice 'limit' is the same no matter the zoom level, since this is like
2107 * remove doubles and could annoying if it joined points when zoomed out.
2108 * 'penalty' is in screen pixel space otherwise zooming in on a uv-vert and
2109 * shift-selecting can consider an adjacent point close enough to add to
2110 * the selection rather than de-selecting the closest. */
2112 uvedit_pixel_to_float(sima, limit, 0.05f);
2113 uvedit_pixel_to_float(sima, penalty, 5.0f / (sima ? sima->zoom : 1.0f));
2115 /* retrieve operation mode */
2116 if (ts->uv_flag & UV_SYNC_SELECTION) {
2119 if (ts->selectmode & SCE_SELECT_FACE)
2120 selectmode = UV_SELECT_FACE;
2121 else if (ts->selectmode & SCE_SELECT_EDGE)
2122 selectmode = UV_SELECT_EDGE;
2124 selectmode = UV_SELECT_VERTEX;
2126 sticky = SI_STICKY_DISABLE;
2130 selectmode = ts->uv_selectmode;
2131 sticky = (sima) ? sima->sticky : 1;
2134 /* find nearest element */
2137 uv_find_nearest_edge(scene, ima, em, co, &hit);
2138 if (hit.efa == NULL) {
2139 BLI_array_free(hitv);
2140 BLI_array_free(hituv);
2141 return OPERATOR_CANCELLED;
2146 else if (selectmode == UV_SELECT_VERTEX) {
2148 uv_find_nearest_vert(scene, ima, em, co, penalty, &hit);
2149 if (hit.efa == NULL) {
2150 BLI_array_free(hitv);
2151 BLI_array_free(hituv);
2152 return OPERATOR_CANCELLED;
2155 /* mark 1 vertex as being hit */
2156 BLI_array_grow_items(hitv, hit.efa->len);
2157 BLI_array_grow_items(hituv, hit.efa->len);
2158 for (i = 0; i < hit.efa->len; i++) {
2159 hitv[i] = 0xFFFFFFFF;
2162 hitv[hit.lindex] = hit.vert1;
2163 hituv[hit.lindex] = hit.luv->uv;
2165 hitlen = hit.efa->len;
2167 else if (selectmode == UV_SELECT_EDGE) {
2169 uv_find_nearest_edge(scene, ima, em, co, &hit);
2170 if (hit.efa == NULL) {
2171 BLI_array_free(hitv);
2172 BLI_array_free(hituv);
2173 return OPERATOR_CANCELLED;
2176 /* mark 2 edge vertices as being hit */
2177 BLI_array_grow_items(hitv, hit.efa->len);
2178 BLI_array_grow_items(hituv, hit.efa->len);
2179 fill_vn_i(hitv, hit.efa->len, 0xFFFFFFFF);
2181 hitv[hit.lindex] = hit.vert1;
2182 hitv[(hit.lindex + 1) % hit.efa->len] = hit.vert2;
2183 hituv[hit.lindex] = hit.luv->uv;
2184 hituv[(hit.lindex + 1) % hit.efa->len] = hit.luv_next->uv;
2186 hitlen = hit.efa->len;
2188 else if (selectmode == UV_SELECT_FACE) {
2190 uv_find_nearest_face(scene, ima, em, co, &hit);
2191 if (hit.efa == NULL) {
2192 BLI_array_free(hitv);
2193 BLI_array_free(hituv);
2194 return OPERATOR_CANCELLED;
2198 BM_active_face_set(em->bm, hit.efa);
2200 /* mark all face vertices as being hit */
2202 BLI_array_grow_items(hitv, hit.efa->len);
2203 BLI_array_grow_items(hituv, hit.efa->len);
2205 BM_ITER_ELEM (l, &liter, hit.efa, BM_LOOPS_OF_FACE) {
2206 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2208 hitv[i] = BM_elem_index_get(l->v);
2212 hitlen = hit.efa->len;
2214 else if (selectmode == UV_SELECT_ISLAND) {
2215 uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
2217 if (hit.efa == NULL) {
2218 BLI_array_free(hitv);
2219 BLI_array_free(hituv);
2220 return OPERATOR_CANCELLED;
2227 BLI_array_free(hitv);
2228 BLI_array_free(hituv);
2229 return OPERATOR_CANCELLED;
2234 flush = uv_select_edgeloop(scene, ima, em, &hit, limit, extend);
2236 else if (selectmode == UV_SELECT_ISLAND) {
2237 uv_select_linked(scene, ima, em, limit, &hit, extend);
2240 if (selectmode == UV_SELECT_VERTEX) {
2241 /* (de)select uv vertex */
2242 select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
2243 uvedit_uv_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset);
2246 else if (selectmode == UV_SELECT_EDGE) {
2247 /* (de)select edge */
2248 select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset));
2249 uvedit_edge_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset);
2252 else if (selectmode == UV_SELECT_FACE) {
2253 /* (de)select face */
2254 select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset));
2255 uvedit_face_select_set(scene, em, hit.efa, select, true, cd_loop_uv_offset);
2259 /* de-selecting an edge may deselect a face too - validate */
2261 if (select == FALSE) {
2262 BM_select_history_validate(em->bm);
2266 /* (de)select sticky uv nodes */
2267 if (sticky != SI_STICKY_DISABLE) {
2269 BM_mesh_elem_index_ensure(em->bm, BM_VERT);
2271 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2272 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2273 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2276 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2277 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2278 if (uv_sticky_select(limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen))
2279 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2283 flush = select ? 1 : -1;
2288 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
2290 if (selectmode == UV_SELECT_VERTEX) {
2292 uvedit_uv_select_enable(em, scene, hit.l, true, cd_loop_uv_offset);
2295 else if (selectmode == UV_SELECT_EDGE) {
2297 uvedit_edge_select_enable(em, scene, hit.l, true, cd_loop_uv_offset);
2300 else if (selectmode == UV_SELECT_FACE) {
2302 uvedit_face_select_enable(scene, em, hit.efa, true, cd_loop_uv_offset);
2305 /* select sticky uvs */
2306 if (sticky != SI_STICKY_DISABLE) {
2307 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2308 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2309 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2312 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2313 if (sticky == SI_STICKY_DISABLE) continue;
2314 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2316 if (uv_sticky_select(limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen))
2317 uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset);
2326 /* flush for mesh selection */
2330 if (ts->selectmode != SCE_SELECT_FACE) {
2331 if (flush == 1) EDBM_select_flush(em);
2332 else if (flush == -1) EDBM_deselect_flush(em);
2337 /* push vertex -> edge selection */
2339 EDBM_select_flush(em);
2342 EDBM_deselect_flush(em);
2346 EDBM_selectmode_flush(em);
2352 DAG_id_tag_update(obedit->data, 0);
2353 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2355 BLI_array_free(hitv);
2356 BLI_array_free(hituv);
2358 return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
2361 static int uv_select_exec(bContext *C, wmOperator *op)
2366 RNA_float_get_array(op->ptr, "location", co);
2367 extend = RNA_boolean_get(op->ptr, "extend");
2370 return uv_mouse_select(C, co, extend, loop);
2373 static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2375 ARegion *ar = CTX_wm_region(C);
2378 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2379 RNA_float_set_array(op->ptr, "location", co);
2381 return uv_select_exec(C, op);
2384 static void UV_OT_select(wmOperatorType *ot)
2387 ot->name = "Select";
2388 ot->description = "Select UV vertices";
2389 ot->idname = "UV_OT_select";
2390 ot->flag = OPTYPE_UNDO;
2393 ot->exec = uv_select_exec;
2394 ot->invoke = uv_select_invoke;
2395 ot->poll = ED_operator_uvedit; /* requires space image */;
2398 RNA_def_boolean(ot->srna, "extend", 0,
2399 "Extend", "Extend selection rather than clearing the existing selection");
2400 RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2401 "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2404 /* ******************** loop select operator **************** */
2406 static int uv_select_loop_exec(bContext *C, wmOperator *op)
2411 RNA_float_get_array(op->ptr, "location", co);
2412 extend = RNA_boolean_get(op->ptr, "extend");
2415 return uv_mouse_select(C, co, extend, loop);
2418 static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2420 ARegion *ar = CTX_wm_region(C);
2423 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2424 RNA_float_set_array(op->ptr, "location", co);
2426 return uv_select_loop_exec(C, op);
2429 static void UV_OT_select_loop(wmOperatorType *ot)
2432 ot->name = "Loop Select";
2433 ot->description = "Select a loop of connected UV vertices";
2434 ot->idname = "UV_OT_select_loop";
2435 ot->flag = OPTYPE_UNDO;
2438 ot->exec = uv_select_loop_exec;
2439 ot->invoke = uv_select_loop_invoke;
2440 ot->poll = ED_operator_uvedit; /* requires space image */;
2443 RNA_def_boolean(ot->srna, "extend", 0,
2444 "Extend", "Extend selection rather than clearing the existing selection");
2445 RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2446 "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2449 /* ******************** linked select operator **************** */
2451 static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, int pick)
2453 SpaceImage *sima = CTX_wm_space_image(C);
2454 Scene *scene = CTX_data_scene(C);
2455 ToolSettings *ts = scene->toolsettings;
2456 Object *obedit = CTX_data_edit_object(C);
2457 Image *ima = CTX_data_edit_image(C);
2458 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2462 NearestHit hit, *hit_p = NULL;
2464 if (ts->uv_flag & UV_SYNC_SELECTION) {
2465 BKE_report(op->reports, RPT_ERROR, "Cannot select linked when sync selection is enabled");
2466 return OPERATOR_CANCELLED;
2469 extend = RNA_boolean_get(op->ptr, "extend");
2470 uvedit_pixel_to_float(sima, limit, 0.05f);
2477 ARegion *ar = CTX_wm_region(C);
2479 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2480 RNA_float_set_array(op->ptr, "location", co);
2484 RNA_float_get_array(op->ptr, "location", co);
2487 uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
2491 uv_select_linked(scene, ima, em, limit, hit_p, extend);
2493 DAG_id_tag_update(obedit->data, 0);
2494 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2496 return OPERATOR_FINISHED;
2499 static int uv_select_linked_exec(bContext *C, wmOperator *op)
2501 return uv_select_linked_internal(C, op, NULL, 0);
2504 static void UV_OT_select_linked(wmOperatorType *ot)
2507 ot->name = "Select Linked";
2508 ot->description = "Select all UV vertices linked to the active UV map";
2509 ot->idname = "UV_OT_select_linked";
2510 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2513 ot->exec = uv_select_linked_exec;
2514 ot->poll = ED_operator_uvedit; /* requires space image */
2517 RNA_def_boolean(ot->srna, "extend", 0,
2518 "Extend", "Extend selection rather than clearing the existing selection");
2521 static int uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2523 return uv_select_linked_internal(C, op, event, 1);
2526 static int uv_select_linked_pick_exec(bContext *C, wmOperator *op)
2528 return uv_select_linked_internal(C, op, NULL, 1);
2531 static void UV_OT_select_linked_pick(wmOperatorType *ot)
2534 ot->name = "Select Linked Pick";
2535 ot->description = "Select all UV vertices linked under the mouse";
2536 ot->idname = "UV_OT_select_linked_pick";
2537 ot->flag = OPTYPE_UNDO;
2540 ot->invoke = uv_select_linked_pick_invoke;
2541 ot->exec = uv_select_linked_pick_exec;
2542 ot->poll = ED_operator_uvedit; /* requires space image */;
2545 RNA_def_boolean(ot->srna, "extend", 0,
2546 "Extend", "Extend selection rather than clearing the existing selection");
2548 RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
2549 "Location", "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", -100.0f, 100.0f);
2552 /* note: this is based on similar use case to MESH_OT_split(), which has a similar effect
2553 * but in this case they are not joined to begin with (only having the behavior of being joined)
2554 * so its best to call this uv_select_split() instead of just split(), but assigned to the same key
2555 * as MESH_OT_split - Campbell */
2556 static int uv_select_split_exec(bContext *C, wmOperator *op)
2558 Scene *scene = CTX_data_scene(C);
2559 ToolSettings *ts = scene->toolsettings;
2560 Image *ima = CTX_data_edit_image(C);
2561 Object *obedit = CTX_data_edit_object(C);
2562 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2569 bool change = false;
2571 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
2572 const int cd_poly_tex_offset = CustomData_get_offset(&bm->pdata, CD_MTEXPOLY);
2574 if (ts->uv_flag & UV_SYNC_SELECTION) {
2575 BKE_report(op->reports, RPT_ERROR, "Cannot split selection when sync selection is enabled");
2576 return OPERATOR_CANCELLED;
2581 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2583 int is_unsel = FALSE;
2584 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2586 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2589 /* are we all selected? */
2590 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2591 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2593 if (luv->flag & MLOOPUV_VERTSEL) {
2600 /* we have mixed selection, bail out */
2601 if (is_sel && is_unsel) {
2606 if (is_sel && is_unsel) {
2607 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2608 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2609 luv->flag &= ~MLOOPUV_VERTSEL;
2617 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
2618 return OPERATOR_FINISHED;
2621 return OPERATOR_CANCELLED;
2626 static void UV_OT_select_split(wmOperatorType *ot)
2629 ot->name = "Select Split";
2630 ot->description = "Select only entirely selected faces";
2631 ot->idname = "UV_OT_select_split";
2632 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2635 ot->exec = uv_select_split_exec;
2636 ot->poll = ED_operator_uvedit; /* requires space image */;
2639 /* ******************** unlink selection operator **************** */
2641 static int uv_unlink_selection_exec(bContext *C, wmOperator *op)
2643 Scene *scene = CTX_data_scene(C);
2644 ToolSettings *ts = scene->toolsettings;
2645 Object *obedit = CTX_data_edit_object(C);
2646 Image *ima = CTX_data_edit_image(C);
2647 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2654 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2655 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2657 if (ts->uv_flag & UV_SYNC_SELECTION) {
2658 BKE_report(op->reports, RPT_ERROR, "Cannot unlink selection when sync selection is enabled");
2659 return OPERATOR_CANCELLED;
2662 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2665 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
2666 if (!uvedit_face_visible_test(scene, ima, efa, tf))
2669 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2670 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2672 if (!(luv->flag & MLOOPUV_VERTSEL)) {
2679 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2680 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2681 luv->flag &= ~MLOOPUV_VERTSEL;
2686 DAG_id_tag_update(obedit->data, 0);
2687 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2689 return OPERATOR_FINISHED;
2692 static void UV_OT_unlink_selected(wmOperatorType *ot)
2695 ot->name = "Unlink Selection";
2696 ot->description = "Unlink selected UV vertices from active UV map";
2697 ot->idname = "UV_OT_unlink_selected";
2698 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2701 ot->exec = uv_unlink_selection_exec;
2702 ot->poll = ED_operator_uvedit;
2705 static void uv_select_sync_flush(ToolSettings *ts, BMEditMesh *em, const short select)
2707 /* bmesh API handles flushing but not on de-select */
2708 if (ts->uv_flag & UV_SYNC_SELECTION) {
2709 if (ts->selectmode != SCE_SELECT_FACE) {
2710 if (select == FALSE) {
2711 EDBM_deselect_flush(em);
2714 EDBM_select_flush(em);
2718 if (select == FALSE) {
2719 BM_select_history_validate(em->bm);
2726 /* -------------------------------------------------------------------- */
2727 /* Utility functions to flush the uv-selection from tags */
2730 * helper function for #uv_select_flush_from_tag_loop and uv_select_flush_from_tag_face
2732 static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene, BMEditMesh *em, UvVertMap *vmap,
2733 const unsigned int efa_index, BMLoop *l,
2734 const bool select, const int cd_loop_uv_offset)
2736 UvMapVert *start_vlist = NULL, *vlist_iter;
2739 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2741 vlist_iter = EDBM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
2743 while (vlist_iter) {
2744 if (vlist_iter->separate)
2745 start_vlist = vlist_iter;
2747 if (efa_index == vlist_iter->f)
2750 vlist_iter = vlist_iter->next;
2753 vlist_iter = start_vlist;
2754 while (vlist_iter) {
2756 if (vlist_iter != start_vlist && vlist_iter->separate)
2759 if (efa_index != vlist_iter->f) {
2761 efa_vlist = EDBM_face_at_index(em, vlist_iter->f);
2762 /* tf_vlist = BM_ELEM_CD_GET_VOID_P(efa_vlist, cd_poly_tex_offset); */ /* UNUSED */
2764 l_other = BM_iter_at_index(em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->tfindex);
2766 uvedit_uv_select_set(em, scene, l_other, select, false, cd_loop_uv_offset);
2768 vlist_iter = vlist_iter->next;
2773 * Flush the selection from face tags based on sticky and selection modes.
2775 * needed because settings the selection a face is done in a number of places but it also needs to respect
2776 * the sticky modes for the UV verts, so dealing with the sticky modes is best done in a separate function.
2778 * \note! This function is very similar to #uv_select_flush_from_tag_loop, be sure to update both upon changing.
2780 static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object *obedit, const bool select)
2782 /* Selecting UV Faces with some modes requires us to change
2783 * the selection in other faces (depending on the sticky mode).
2785 * This only needs to be done when the Mesh is not used for
2786 * selection (so for sticky modes, vertex or location based). */
2788 ToolSettings *ts = scene->toolsettings;
2789 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2794 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2796 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
2797 /* Tag all verts as untouched, then touch the ones that have a face center
2798 * in the loop and select all MLoopUV's that use a touched vert. */
2801 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2802 BM_elem_flag_disable(eve, BM_ELEM_TAG);
2805 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2806 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2807 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2808 BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2813 /* now select tagged verts */
2814 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2815 /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2817 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2818 if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
2819 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2824 else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
2825 struct UvVertMap *vmap;
2827 unsigned int efa_index;
2829 uvedit_pixel_to_float(sima, limit, 0.05);
2831 EDBM_index_arrays_ensure(em, BM_FACE);
2832 vmap = EDBM_uv_vert_map_create(em, 0, limit);
2837 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
2838 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2839 /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2841 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2842 uv_select_flush_from_tag_sticky_loc_internal(scene, em, vmap, efa_index, l,
2843 select, cd_loop_uv_offset);
2847 EDBM_uv_vert_map_free(vmap);
2850 else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
2851 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2852 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2853 uvedit_face_select_set(scene, em, efa, select, false, cd_loop_uv_offset);
2862 * Flush the selection from loop tags based on sticky and selection modes.
2864 * needed because settings the selection a face is done in a number of places but it also needs to respect
2865 * the sticky modes for the UV verts, so dealing with the sticky modes is best done in a separate function.
2867 * \note! This function is very similar to #uv_select_flush_from_tag_loop, be sure to update both upon changing.
2869 static void uv_select_flush_from_tag_loop(SpaceImage *sima, Scene *scene, Object *obedit, bool select)
2871 /* Selecting UV Loops with some modes requires us to change
2872 * the selection in other faces (depending on the sticky mode).
2874 * This only needs to be done when the Mesh is not used for
2875 * selection (so for sticky modes, vertex or location based). */
2877 ToolSettings *ts = scene->toolsettings;
2878 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2884 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2887 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
2888 /* Tag all verts as untouched, then touch the ones that have a face center
2889 * in the loop and select all MLoopUV's that use a touched vert. */
2892 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2893 BM_elem_flag_disable(eve, BM_ELEM_TAG);
2896 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2897 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2898 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2899 BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2904 /* now select tagged verts */
2905 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2906 /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2908 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2909 if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
2910 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2915 else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
2916 struct UvVertMap *vmap;
2918 unsigned int efa_index;
2920 uvedit_pixel_to_float(sima, limit, 0.05);
2922 EDBM_index_arrays_ensure(em, BM_FACE);
2923 vmap = EDBM_uv_vert_map_create(em, 0, limit);
2928 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
2929 /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2931 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2932 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2933 uv_select_flush_from_tag_sticky_loc_internal(scene, em, vmap, efa_index, l,
2934 select, cd_loop_uv_offset);
2938 EDBM_uv_vert_map_free(vmap);
2941 else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
2942 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2943 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2944 if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2945 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
2952 /* ******************** border select operator **************** */
2954 static int uv_border_select_exec(bContext *C, wmOperator *op)
2956 SpaceImage *sima = CTX_wm_space_image(C);
2957 Scene *scene = CTX_data_scene(C);
2958 ToolSettings *ts = scene->toolsettings;
2959 Object *obedit = CTX_data_edit_object(C);
2960 Image *ima = CTX_data_edit_image(C);
2961 ARegion *ar = CTX_wm_region(C);
2962 BMEditMesh *em = BKE_editmesh_from_object(obedit);
2970 bool change, pinned, select, extend;
2971 const bool use_face_center = (ts->uv_flag & UV_SYNC_SELECTION) ?
2972 (ts->selectmode == SCE_SELECT_FACE) :
2973 (ts->uv_selectmode == UV_SELECT_FACE);
2975 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2976 const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
2978 /* get rectangle from operator */
2979 WM_operator_properties_border_to_rcti(op, &rect);
2981 UI_view2d_region_to_view(&ar->v2d, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
2982 UI_view2d_region_to_view(&ar->v2d, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
2984 /* figure out what to select/deselect */
2985 select = (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT);
2986 pinned = RNA_boolean_get(op->ptr, "pinned");
2987 extend = RNA_boolean_get(op->ptr, "extend");
2990 uv_select_all_perform(scene, ima, em, SEL_DESELECT);
2992 /* do actual selection */
2993 if (use_face_center && !pinned) {
2994 /* handle face selection mode */
2999 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3000 /* assume not touched */
3001 BM_elem_flag_disable(efa, BM_ELEM_TAG);
3003 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
3004 if (uvedit_face_visible_test(scene, ima, efa, tf)) {
3005 uv_poly_center(efa, cent, cd_loop_uv_offset);
3006 if (BLI_rctf_isect_pt_v(&rectf, cent)) {
3007 BM_elem_flag_enable(efa, BM_ELEM_TAG);
3013 /* (de)selects all tagged faces and deals with sticky modes */
3015 uv_select_flush_from_tag_face(sima, scene, obedit, select);
3019 /* other selection modes */
3022 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3023 tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
3024 if (!uvedit_face_visible_test(scene, ima, efa, tf))
3026 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3027 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3029 if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION) ) {
3031 /* UV_SYNC_SELECTION - can't do pinned selection */
3032 if (BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
3033 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
3037 if ((luv->flag & MLOOPUV_PINNED) && BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
3038 uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset);
3046 uv_select_sync_flush(ts, em, select);