Orientation for 3D cursor
[blender.git] / source / blender / editors / uvedit / uvedit_unwrap_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): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/uvedit/uvedit_unwrap_ops.c
29  *  \ingroup eduv
30  */
31
32
33 #include <string.h>
34 #include <stdlib.h>
35 #include <math.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "DNA_camera_types.h"
40 #include "DNA_mesh_types.h"
41 #include "DNA_meshdata_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_modifier_types.h"
45
46 #include "BLI_utildefines.h"
47 #include "BLI_alloca.h"
48 #include "BLI_math.h"
49 #include "BLI_uvproject.h"
50 #include "BLI_string.h"
51
52 #include "BLT_translation.h"
53
54 #include "BKE_cdderivedmesh.h"
55 #include "BKE_subsurf.h"
56 #include "BKE_context.h"
57 #include "BKE_customdata.h"
58 #include "BKE_image.h"
59 #include "BKE_main.h"
60 #include "BKE_material.h"
61 #include "BKE_report.h"
62 #include "BKE_scene.h"
63 #include "BKE_editmesh.h"
64 #include "BKE_layer.h"
65
66 #include "DEG_depsgraph.h"
67
68 #include "PIL_time.h"
69
70 #include "UI_interface.h"
71
72 #include "ED_image.h"
73 #include "ED_mesh.h"
74 #include "ED_screen.h"
75 #include "ED_uvedit.h"
76 #include "ED_view3d.h"
77
78 #include "RNA_access.h"
79 #include "RNA_define.h"
80
81
82 #include "WM_api.h"
83 #include "WM_types.h"
84
85 #include "uvedit_intern.h"
86 #include "uvedit_parametrizer.h"
87
88 static void modifier_unwrap_state(Object *obedit, Scene *scene, bool *r_use_subsurf)
89 {
90         ModifierData *md;
91         bool subsurf = (scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF) != 0;
92
93         md = obedit->modifiers.first;
94
95         /* subsurf will take the modifier settings only if modifier is first or right after mirror */
96         if (subsurf) {
97                 if (md && md->type == eModifierType_Subsurf)
98                         subsurf = true;
99                 else
100                         subsurf = false;
101         }
102
103         *r_use_subsurf = subsurf;
104 }
105
106 static bool ED_uvedit_ensure_uvs(bContext *C, Scene *UNUSED(scene), Object *obedit)
107 {
108         BMEditMesh *em = BKE_editmesh_from_object(obedit);
109         BMFace *efa;
110         BMIter iter;
111         Image *ima;
112         bScreen *sc;
113         ScrArea *sa;
114         SpaceLink *slink;
115         SpaceImage *sima;
116         int cd_loop_uv_offset;
117
118         if (ED_uvedit_test(obedit))
119                 return 1;
120
121         if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV))
122                 ED_mesh_uv_texture_add(obedit->data, NULL, true);
123
124         if (!ED_uvedit_test(obedit))
125                 return 0;
126
127         cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
128
129         ima = CTX_data_edit_image(C);
130
131         if (!ima) {
132                 /* no image in context in the 3d view, we find first image window .. */
133                 sc = CTX_wm_screen(C);
134
135                 for (sa = sc->areabase.first; sa; sa = sa->next) {
136                         slink = sa->spacedata.first;
137                         if (slink->spacetype == SPACE_IMAGE) {
138                                 sima = (SpaceImage *)slink;
139
140                                 ima = sima->image;
141                                 if (ima) {
142                                         if (ima->type == IMA_TYPE_R_RESULT || ima->type == IMA_TYPE_COMPOSITE)
143                                                 ima = NULL;
144                                         else
145                                                 break;
146                                 }
147                         }
148                 }
149         }
150         
151         /* select new UV's (ignore UV_SYNC_SELECTION in this case) */
152         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
153                 BMIter liter;
154                 BMLoop *l;
155
156                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
157                         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
158                         luv->flag |= MLOOPUV_VERTSEL;
159                 }
160         }
161
162         return 1;
163 }
164
165 /****************** Parametrizer Conversion ***************/
166
167 static bool uvedit_have_selection(Scene *scene, BMEditMesh *em, bool implicit)
168 {
169         BMFace *efa;
170         BMLoop *l;
171         BMIter iter, liter;
172         const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
173         
174         if (cd_loop_uv_offset == -1) {
175                 return (em->bm->totfacesel != 0);
176         }
177
178         /* verify if we have any selected uv's before unwrapping,
179          * so we can cancel the operator early */
180         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
181                 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
182                         if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
183                                 continue;
184                 }
185                 else if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
186                         continue;
187         
188                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
189                         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))
190                                 break;
191                 }
192                 
193                 if (implicit && !l)
194                         continue;
195                 
196                 return true;
197         }
198
199         return false;
200 }
201
202 static bool uvedit_have_selection_multi(
203         Scene *scene, Object **objects, const uint objects_len, bool implicit)
204 {
205         bool have_select = false;
206         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
207                 Object *obedit = objects[ob_index];
208                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
209                 if (uvedit_have_selection(scene, em, implicit)) {
210                         have_select = true;
211                         break;
212                 }
213         }
214         return have_select;
215 }
216
217 void ED_uvedit_get_aspect(Scene *UNUSED(scene), Object *ob, BMesh *bm, float *aspx, float *aspy)
218 {
219         bool sloppy = true;
220         bool selected = false;
221         BMFace *efa;
222         Image *ima;
223
224         efa = BM_mesh_active_face_get(bm, sloppy, selected);
225
226         if (efa) {
227                 ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
228
229                 ED_image_get_uv_aspect(ima, NULL, aspx, aspy);
230         }
231         else {
232                 *aspx = 1.0f;
233                 *aspy = 1.0f;
234         }
235 }
236
237 static void construct_param_handle_face_add(ParamHandle *handle, Scene *scene,
238                                             BMFace *efa, int face_index, const int cd_loop_uv_offset)
239 {
240         ParamKey key;
241         ParamKey *vkeys = BLI_array_alloca(vkeys, efa->len);
242         ParamBool *pin = BLI_array_alloca(pin, efa->len);
243         ParamBool *select = BLI_array_alloca(select, efa->len);
244         float **co = BLI_array_alloca(co, efa->len);
245         float **uv = BLI_array_alloca(uv, efa->len);
246         int i;
247
248         BMIter liter;
249         BMLoop *l;
250
251         key = (ParamKey)face_index;
252
253         /* let parametrizer split the ngon, it can make better decisions
254          * about which split is best for unwrapping than scanfill */
255         BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
256                 MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
257
258                 vkeys[i] = (ParamKey)BM_elem_index_get(l->v);
259                 co[i] = l->v->co;
260                 uv[i] = luv->uv;
261                 pin[i] = (luv->flag & MLOOPUV_PINNED) != 0;
262                 select[i] = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
263         }
264
265         param_face_add(handle, key, i, vkeys, co, uv, pin, select, efa->no);
266 }
267
268 /* See: construct_param_handle_multi to handle multiple objects at once. */
269 static ParamHandle *construct_param_handle(
270         Scene *scene, Object *ob, BMesh *bm,
271         const bool implicit, const bool fill, const bool sel,
272         const bool correct_aspect)
273 {
274         ParamHandle *handle;
275         BMFace *efa;
276         BMLoop *l;
277         BMEdge *eed;
278         BMIter iter, liter;
279         int i;
280         
281         const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
282
283         handle = param_construct_begin();
284
285         if (correct_aspect) {
286                 float aspx, aspy;
287
288                 ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy);
289
290                 if (aspx != aspy)
291                         param_aspect_ratio(handle, aspx, aspy);
292         }
293         
294         /* we need the vert indices */
295         BM_mesh_elem_index_ensure(bm, BM_VERT);
296         
297         BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
298
299                 if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || (sel && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
300                         continue;
301                 }
302
303                 if (implicit) {
304                         bool is_loopsel = false;
305
306                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
307                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
308                                         is_loopsel = true;
309                                         break;
310                                 }
311                         }
312                         if (is_loopsel == false) {
313                                 continue;
314                         }
315                 }
316
317                 construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset);
318         }
319
320         if (!implicit) {
321                 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
322                         if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
323                                 ParamKey vkeys[2];
324                                 vkeys[0] = (ParamKey)BM_elem_index_get(eed->v1);
325                                 vkeys[1] = (ParamKey)BM_elem_index_get(eed->v2);
326                                 param_edge_set_seam(handle, vkeys);
327                         }
328                 }
329         }
330
331         param_construct_end(handle, fill, implicit);
332
333         return handle;
334 }
335
336 /**
337  * Version of #construct_param_handle_single that handles multiple objects.
338  */
339 static ParamHandle *construct_param_handle_multi(
340         Scene *scene, Object **objects, const uint objects_len,
341         const bool implicit, const bool fill, const bool sel,
342         const bool correct_aspect)
343 {
344         ParamHandle *handle;
345         BMFace *efa;
346         BMLoop *l;
347         BMEdge *eed;
348         BMIter iter, liter;
349         int i;
350
351
352         handle = param_construct_begin();
353
354         if (correct_aspect) {
355                 Object *ob = objects[0];
356                 BMEditMesh *em = BKE_editmesh_from_object(ob);
357                 BMesh *bm = em->bm;
358                 float aspx, aspy;
359
360                 ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy);
361                 if (aspx != aspy) {
362                         param_aspect_ratio(handle, aspx, aspy);
363                 }
364         }
365
366         /* we need the vert indices */
367         EDBM_mesh_elem_index_ensure_multi(objects, objects_len, BM_VERT);
368
369         int offset = 0;
370
371         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
372                 Object *obedit = objects[ob_index];
373                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
374                 BMesh *bm = em->bm;
375
376                 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
377
378                 BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
379
380                         if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || (sel && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
381                                 continue;
382                         }
383
384                         if (implicit) {
385                                 bool is_loopsel = false;
386
387                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
388                                         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
389                                                 is_loopsel = true;
390                                                 break;
391                                         }
392                                 }
393                                 if (is_loopsel == false) {
394                                         continue;
395                                 }
396                         }
397
398                         construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset);
399                 }
400
401                 if (!implicit) {
402                         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
403                                 if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
404                                         ParamKey vkeys[2];
405                                         vkeys[0] = (ParamKey)BM_elem_index_get(eed->v1);
406                                         vkeys[1] = (ParamKey)BM_elem_index_get(eed->v2);
407                                         param_edge_set_seam(handle, vkeys);
408                                 }
409                         }
410                 }
411                 offset += bm->totface;
412         }
413
414         param_construct_end(handle, fill, implicit);
415
416         return handle;
417 }
418
419
420 static void texface_from_original_index(BMFace *efa, int index, float **uv, ParamBool *pin, ParamBool *select,
421                                         Scene *scene, const int cd_loop_uv_offset)
422 {
423         BMLoop *l;
424         BMIter liter;
425         MLoopUV *luv;
426
427         *uv = NULL;
428         *pin = 0;
429         *select = 1;
430
431         if (index == ORIGINDEX_NONE)
432                 return;
433
434         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
435                 if (BM_elem_index_get(l->v) == index) {
436                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
437                         *uv = luv->uv;
438                         *pin = (luv->flag & MLOOPUV_PINNED) ? 1 : 0;
439                         *select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
440                         break;
441                 }
442         }
443 }
444
445 /* unwrap handle initialization for subsurf aware-unwrapper. The many modifications required to make the original function(see above)
446  * work justified the existence of a new function. */
447 static ParamHandle *construct_param_handle_subsurfed(Scene *scene, Object *ob, BMEditMesh *em, short fill, short sel, short correct_aspect)
448 {
449         ParamHandle *handle;
450         /* index pointers */
451         MPoly *mpoly;
452         MLoop *mloop;
453         MEdge *edge;
454         int i;
455
456         /* pointers to modifier data for unwrap control */
457         ModifierData *md;
458         SubsurfModifierData *smd_real;
459         /* modifier initialization data, will  control what type of subdivision will happen*/
460         SubsurfModifierData smd = {{NULL}};
461         /* Used to hold subsurfed Mesh */
462         DerivedMesh *derivedMesh, *initialDerived;
463         /* holds original indices for subsurfed mesh */
464         const int *origVertIndices, *origEdgeIndices, *origPolyIndices;
465         /* Holds vertices of subsurfed mesh */
466         MVert *subsurfedVerts;
467         MEdge *subsurfedEdges;
468         MPoly *subsurfedPolys;
469         MLoop *subsurfedLoops;
470         /* number of vertices and faces for subsurfed mesh*/
471         int numOfEdges, numOfFaces;
472
473         /* holds a map to editfaces for every subsurfed MFace. These will be used to get hidden/ selected flags etc. */
474         BMFace **faceMap;
475         /* similar to the above, we need a way to map edges to their original ones */
476         BMEdge **edgeMap;
477
478         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
479
480         handle = param_construct_begin();
481
482         if (correct_aspect) {
483                 float aspx, aspy;
484
485                 ED_uvedit_get_aspect(scene, ob, em->bm, &aspx, &aspy);
486
487                 if (aspx != aspy)
488                         param_aspect_ratio(handle, aspx, aspy);
489         }
490
491         /* number of subdivisions to perform */
492         md = ob->modifiers.first;
493         smd_real = (SubsurfModifierData *)md;
494
495         smd.levels = smd_real->levels;
496         smd.subdivType = smd_real->subdivType;
497                 
498         initialDerived = CDDM_from_editbmesh(em, false, false);
499         derivedMesh = subsurf_make_derived_from_derived(initialDerived, &smd,
500                                                         NULL, SUBSURF_IN_EDIT_MODE);
501
502         initialDerived->release(initialDerived);
503
504         /* get the derived data */
505         subsurfedVerts = derivedMesh->getVertArray(derivedMesh);
506         subsurfedEdges = derivedMesh->getEdgeArray(derivedMesh);
507         subsurfedPolys = derivedMesh->getPolyArray(derivedMesh);
508         subsurfedLoops = derivedMesh->getLoopArray(derivedMesh);
509
510         origVertIndices = derivedMesh->getVertDataArray(derivedMesh, CD_ORIGINDEX);
511         origEdgeIndices = derivedMesh->getEdgeDataArray(derivedMesh, CD_ORIGINDEX);
512         origPolyIndices = derivedMesh->getPolyDataArray(derivedMesh, CD_ORIGINDEX);
513
514         numOfEdges = derivedMesh->getNumEdges(derivedMesh);
515         numOfFaces = derivedMesh->getNumPolys(derivedMesh);
516
517         faceMap = MEM_mallocN(numOfFaces * sizeof(BMFace *), "unwrap_edit_face_map");
518
519         BM_mesh_elem_index_ensure(em->bm, BM_VERT);
520         BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_FACE);
521
522         /* map subsurfed faces to original editFaces */
523         for (i = 0; i < numOfFaces; i++)
524                 faceMap[i] = BM_face_at_index(em->bm, origPolyIndices[i]);
525
526         edgeMap = MEM_mallocN(numOfEdges * sizeof(BMEdge *), "unwrap_edit_edge_map");
527
528         /* map subsurfed edges to original editEdges */
529         for (i = 0; i < numOfEdges; i++) {
530                 /* not all edges correspond to an old edge */
531                 edgeMap[i] = (origEdgeIndices[i] != ORIGINDEX_NONE) ?
532                              BM_edge_at_index(em->bm, origEdgeIndices[i]) : NULL;
533         }
534
535         /* Prepare and feed faces to the solver */
536         for (i = 0, mpoly = subsurfedPolys; i < numOfFaces; i++, mpoly++) {
537                 ParamKey key, vkeys[4];
538                 ParamBool pin[4], select[4];
539                 float *co[4];
540                 float *uv[4];
541                 BMFace *origFace = faceMap[i];
542
543                 if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
544                         if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN))
545                                 continue;
546                 }
547                 else {
548                         if (BM_elem_flag_test(origFace, BM_ELEM_HIDDEN) || (sel && !BM_elem_flag_test(origFace, BM_ELEM_SELECT)))
549                                 continue;
550                 }
551
552                 mloop = &subsurfedLoops[mpoly->loopstart];
553
554                 /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */
555                 BLI_assert(mpoly->totloop == 4);
556                 key = (ParamKey)i;
557                 vkeys[0] = (ParamKey)mloop[0].v;
558                 vkeys[1] = (ParamKey)mloop[1].v;
559                 vkeys[2] = (ParamKey)mloop[2].v;
560                 vkeys[3] = (ParamKey)mloop[3].v;
561
562                 co[0] = subsurfedVerts[mloop[0].v].co;
563                 co[1] = subsurfedVerts[mloop[1].v].co;
564                 co[2] = subsurfedVerts[mloop[2].v].co;
565                 co[3] = subsurfedVerts[mloop[3].v].co;
566                 
567                 /* This is where all the magic is done. If the vertex exists in the, we pass the original uv pointer to the solver, thus
568                  * flushing the solution to the edit mesh. */
569                 texface_from_original_index(origFace, origVertIndices[mloop[0].v], &uv[0], &pin[0], &select[0], scene, cd_loop_uv_offset);
570                 texface_from_original_index(origFace, origVertIndices[mloop[1].v], &uv[1], &pin[1], &select[1], scene, cd_loop_uv_offset);
571                 texface_from_original_index(origFace, origVertIndices[mloop[2].v], &uv[2], &pin[2], &select[2], scene, cd_loop_uv_offset);
572                 texface_from_original_index(origFace, origVertIndices[mloop[3].v], &uv[3], &pin[3], &select[3], scene, cd_loop_uv_offset);
573
574                 param_face_add(handle, key, 4, vkeys, co, uv, pin, select, NULL);
575         }
576
577         /* these are calculated from original mesh too */
578         for (edge = subsurfedEdges, i = 0; i < numOfEdges; i++, edge++) {
579                 if ((edgeMap[i] != NULL) && BM_elem_flag_test(edgeMap[i], BM_ELEM_SEAM)) {
580                         ParamKey vkeys[2];
581                         vkeys[0] = (ParamKey)edge->v1;
582                         vkeys[1] = (ParamKey)edge->v2;
583                         param_edge_set_seam(handle, vkeys);
584                 }
585         }
586
587         param_construct_end(handle, fill, 0);
588
589         /* cleanup */
590         MEM_freeN(faceMap);
591         MEM_freeN(edgeMap);
592         derivedMesh->release(derivedMesh);
593
594         return handle;
595 }
596
597 /* ******************** Minimize Stretch operator **************** */
598
599 typedef struct MinStretch {
600         Scene *scene;
601         Object *obedit;
602         BMEditMesh *em;
603         ParamHandle *handle;
604         float blend;
605         double lasttime;
606         int i, iterations;
607         wmTimer *timer;
608 } MinStretch;
609
610 static bool minimize_stretch_init(bContext *C, wmOperator *op)
611 {
612         Scene *scene = CTX_data_scene(C);
613         Object *obedit = CTX_data_edit_object(C);
614         BMEditMesh *em = BKE_editmesh_from_object(obedit);
615         MinStretch *ms;
616         const bool fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
617         bool implicit = true;
618
619         if (!uvedit_have_selection(scene, em, implicit)) {
620                 return false;
621         }
622
623         ms = MEM_callocN(sizeof(MinStretch), "MinStretch");
624         ms->scene = scene;
625         ms->obedit = obedit;
626         ms->em = em;
627         ms->blend = RNA_float_get(op->ptr, "blend");
628         ms->iterations = RNA_int_get(op->ptr, "iterations");
629         ms->i = 0;
630         ms->handle = construct_param_handle(scene, obedit, em->bm, implicit, fill_holes, 1, 1);
631         ms->lasttime = PIL_check_seconds_timer();
632
633         param_stretch_begin(ms->handle);
634         if (ms->blend != 0.0f)
635                 param_stretch_blend(ms->handle, ms->blend);
636
637         op->customdata = ms;
638
639         return true;
640 }
641
642 static void minimize_stretch_iteration(bContext *C, wmOperator *op, bool interactive)
643 {
644         MinStretch *ms = op->customdata;
645         ScrArea *sa = CTX_wm_area(C);
646
647         param_stretch_blend(ms->handle, ms->blend);
648         param_stretch_iter(ms->handle);
649
650         ms->i++;
651         RNA_int_set(op->ptr, "iterations", ms->i);
652
653         if (interactive && (PIL_check_seconds_timer() - ms->lasttime > 0.5)) {
654                 char str[UI_MAX_DRAW_STR];
655
656                 param_flush(ms->handle);
657
658                 if (sa) {
659                         BLI_snprintf(str, sizeof(str),
660                                      IFACE_("Minimize Stretch. Blend %.2f (Press + and -, or scroll wheel to set)"), ms->blend);
661                         ED_area_headerprint(sa, str);
662                 }
663
664                 ms->lasttime = PIL_check_seconds_timer();
665
666                 DEG_id_tag_update(ms->obedit->data, 0);
667                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ms->obedit->data);
668         }
669 }
670
671 static void minimize_stretch_exit(bContext *C, wmOperator *op, bool cancel)
672 {
673         MinStretch *ms = op->customdata;
674         ScrArea *sa = CTX_wm_area(C);
675
676         if (sa)
677                 ED_area_headerprint(sa, NULL);
678         if (ms->timer)
679                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), ms->timer);
680
681         if (cancel)
682                 param_flush_restore(ms->handle);
683         else
684                 param_flush(ms->handle);
685
686         param_stretch_end(ms->handle);
687         param_delete(ms->handle);
688
689         DEG_id_tag_update(ms->obedit->data, 0);
690         WM_event_add_notifier(C, NC_GEOM | ND_DATA, ms->obedit->data);
691
692         MEM_freeN(ms);
693         op->customdata = NULL;
694 }
695
696 static int minimize_stretch_exec(bContext *C, wmOperator *op)
697 {
698         int i, iterations;
699
700         if (!minimize_stretch_init(C, op))
701                 return OPERATOR_CANCELLED;
702
703         iterations = RNA_int_get(op->ptr, "iterations");
704         for (i = 0; i < iterations; i++)
705                 minimize_stretch_iteration(C, op, false);
706         minimize_stretch_exit(C, op, false);
707
708         return OPERATOR_FINISHED;
709 }
710
711 static int minimize_stretch_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
712 {
713         MinStretch *ms;
714
715         if (!minimize_stretch_init(C, op))
716                 return OPERATOR_CANCELLED;
717
718         minimize_stretch_iteration(C, op, true);
719
720         ms = op->customdata;
721         WM_event_add_modal_handler(C, op);
722         ms->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
723
724         return OPERATOR_RUNNING_MODAL;
725 }
726
727 static int minimize_stretch_modal(bContext *C, wmOperator *op, const wmEvent *event)
728 {
729         MinStretch *ms = op->customdata;
730
731         switch (event->type) {
732                 case ESCKEY:
733                 case RIGHTMOUSE:
734                         minimize_stretch_exit(C, op, true);
735                         return OPERATOR_CANCELLED;
736                 case RETKEY:
737                 case PADENTER:
738                 case LEFTMOUSE:
739                         minimize_stretch_exit(C, op, false);
740                         return OPERATOR_FINISHED;
741                 case PADPLUSKEY:
742                 case WHEELUPMOUSE:
743                         if (event->val == KM_PRESS) {
744                                 if (ms->blend < 0.95f) {
745                                         ms->blend += 0.1f;
746                                         ms->lasttime = 0.0f;
747                                         RNA_float_set(op->ptr, "blend", ms->blend);
748                                         minimize_stretch_iteration(C, op, true);
749                                 }
750                         }
751                         break;
752                 case PADMINUS:
753                 case WHEELDOWNMOUSE:
754                         if (event->val == KM_PRESS) {
755                                 if (ms->blend > 0.05f) {
756                                         ms->blend -= 0.1f;
757                                         ms->lasttime = 0.0f;
758                                         RNA_float_set(op->ptr, "blend", ms->blend);
759                                         minimize_stretch_iteration(C, op, true);
760                                 }
761                         }
762                         break;
763                 case TIMER:
764                         if (ms->timer == event->customdata) {
765                                 double start = PIL_check_seconds_timer();
766
767                                 do {
768                                         minimize_stretch_iteration(C, op, true);
769                                 } while (PIL_check_seconds_timer() - start < 0.01);
770                         }
771                         break;
772         }
773
774         if (ms->iterations && ms->i >= ms->iterations) {
775                 minimize_stretch_exit(C, op, false);
776                 return OPERATOR_FINISHED;
777         }
778
779         return OPERATOR_RUNNING_MODAL;
780 }
781
782 static void minimize_stretch_cancel(bContext *C, wmOperator *op)
783 {
784         minimize_stretch_exit(C, op, true);
785 }
786
787 void UV_OT_minimize_stretch(wmOperatorType *ot)
788 {
789         /* identifiers */
790         ot->name = "Minimize Stretch";
791         ot->idname = "UV_OT_minimize_stretch";
792         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING;
793         ot->description = "Reduce UV stretching by relaxing angles";
794         
795         /* api callbacks */
796         ot->exec = minimize_stretch_exec;
797         ot->invoke = minimize_stretch_invoke;
798         ot->modal = minimize_stretch_modal;
799         ot->cancel = minimize_stretch_cancel;
800         ot->poll = ED_operator_uvedit;
801
802         /* properties */
803         RNA_def_boolean(ot->srna, "fill_holes", 1, "Fill Holes", "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry");
804         RNA_def_float_factor(ot->srna, "blend", 0.0f, 0.0f, 1.0f, "Blend", "Blend factor between stretch minimized and original", 0.0f, 1.0f);
805         RNA_def_int(ot->srna, "iterations", 0, 0, INT_MAX, "Iterations", "Number of iterations to run, 0 is unlimited when run interactively", 0, 100);
806 }
807
808 /* ******************** Pack Islands operator **************** */
809
810
811 void ED_uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm, bool selected, bool correct_aspect, bool do_rotate)
812 {
813         ParamHandle *handle;
814         handle = construct_param_handle(scene, ob, bm, true, false, selected, correct_aspect);
815         param_pack(handle, scene->toolsettings->uvcalc_margin, do_rotate);
816         param_flush(handle);
817         param_delete(handle);
818 }
819
820 void ED_uvedit_pack_islands_multi(
821         Scene *scene, Object **objects, const uint objects_len,
822         bool selected, bool correct_aspect, bool do_rotate)
823 {
824         ParamHandle *handle;
825         handle = construct_param_handle_multi(
826                 scene, objects, objects_len, true, false, selected, correct_aspect);
827         param_pack(handle, scene->toolsettings->uvcalc_margin, do_rotate);
828         param_flush(handle);
829         param_delete(handle);
830 }
831
832 static int pack_islands_exec(bContext *C, wmOperator *op)
833 {
834         ViewLayer *view_layer = CTX_data_view_layer(C);
835         Scene *scene = CTX_data_scene(C);
836         bool do_rotate = RNA_boolean_get(op->ptr, "rotate");
837
838         uint objects_len = 0;
839         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len);
840
841         if (!uvedit_have_selection_multi(scene, objects, objects_len, true)) {
842                 MEM_SAFE_FREE(objects);
843                 return OPERATOR_CANCELLED;
844         }
845
846         if (RNA_struct_property_is_set(op->ptr, "margin"))
847                 scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
848         else
849                 RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
850
851         ED_uvedit_pack_islands_multi(scene, objects, objects_len, true, true, do_rotate);
852
853         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
854                 Object *obedit = objects[ob_index];
855                 DEG_id_tag_update(obedit->data, 0);
856                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
857         }
858
859         MEM_SAFE_FREE(objects);
860
861         return OPERATOR_FINISHED;
862 }
863
864 void UV_OT_pack_islands(wmOperatorType *ot)
865 {
866         /* identifiers */
867         ot->name = "Pack Islands";
868         ot->idname = "UV_OT_pack_islands";
869         ot->description = "Transform all islands so that they fill up the UV space as much as possible";
870
871         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
872         
873         /* api callbacks */
874         ot->exec = pack_islands_exec;
875         ot->poll = ED_operator_uvedit;
876
877         /* properties */
878         RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit");
879         RNA_def_float_factor(ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
880 }
881
882 /* ******************** Average Islands Scale operator **************** */
883
884 static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
885 {
886         Scene *scene = CTX_data_scene(C);
887         Object *obedit = CTX_data_edit_object(C);
888         BMEditMesh *em = BKE_editmesh_from_object(obedit);
889         ParamHandle *handle;
890         bool implicit = true;
891
892         if (!uvedit_have_selection(scene, em, implicit)) {
893                 return OPERATOR_CANCELLED;
894         }
895
896         handle = construct_param_handle(scene, obedit, em->bm, implicit, 0, 1, 1);
897         param_average(handle);
898         param_flush(handle);
899         param_delete(handle);
900         
901         DEG_id_tag_update(obedit->data, 0);
902         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
903
904         return OPERATOR_FINISHED;
905 }
906
907 void UV_OT_average_islands_scale(wmOperatorType *ot)
908 {
909         /* identifiers */
910         ot->name = "Average Islands Scale";
911         ot->idname = "UV_OT_average_islands_scale";
912         ot->description = "Average the size of separate UV islands, based on their area in 3D space";
913
914         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
915         
916         /* api callbacks */
917         ot->exec = average_islands_scale_exec;
918         ot->poll = ED_operator_uvedit;
919 }
920
921 /**************** Live Unwrap *****************/
922
923 static ParamHandle *liveHandle = NULL;
924
925 void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
926 {
927         BMEditMesh *em = BKE_editmesh_from_object(obedit);
928         const bool abf = (scene->toolsettings->unwrapper == 0);
929         const bool fillholes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0;
930         bool use_subsurf;
931
932         modifier_unwrap_state(obedit, scene, &use_subsurf);
933
934         if (!ED_uvedit_test(obedit)) {
935                 return;
936         }
937
938         if (use_subsurf)
939                 liveHandle = construct_param_handle_subsurfed(scene, obedit, em, fillholes, false, true);
940         else
941                 liveHandle = construct_param_handle(scene, obedit, em->bm, false, fillholes, false, true);
942
943         param_lscm_begin(liveHandle, PARAM_TRUE, abf);
944 }
945
946 void ED_uvedit_live_unwrap_re_solve(void)
947 {
948         if (liveHandle) {
949                 param_lscm_solve(liveHandle);
950                 param_flush(liveHandle);
951         }
952 }
953         
954 void ED_uvedit_live_unwrap_end(short cancel)
955 {
956         if (liveHandle) {
957                 param_lscm_end(liveHandle);
958                 if (cancel)
959                         param_flush_restore(liveHandle);
960                 param_delete(liveHandle);
961                 liveHandle = NULL;
962         }
963 }
964
965 void ED_uvedit_live_unwrap(Scene *scene, Object *obedit)
966 {
967         BMEditMesh *em = BKE_editmesh_from_object(obedit);
968
969         if (scene->toolsettings->edge_mode_live_unwrap &&
970             CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV))
971         {
972                 ED_unwrap_lscm(scene, obedit, false, false); /* unwrap all not just sel */
973         }
974 }
975
976 /*************** UV Map Common Transforms *****************/
977
978 #define VIEW_ON_EQUATOR 0
979 #define VIEW_ON_POLES   1
980 #define ALIGN_TO_OBJECT 2
981
982 #define POLAR_ZX    0
983 #define POLAR_ZY    1
984
985 static void uv_map_transform_calc_bounds(BMEditMesh *em, float r_min[3], float r_max[3])
986 {
987         BMFace *efa;
988         BMIter iter;
989         INIT_MINMAX(r_min, r_max);
990         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
991                 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
992                         BM_face_calc_bounds_expand(efa, r_min, r_max);
993                 }
994         }
995 }
996
997 static void uv_map_transform_calc_center_median(BMEditMesh *em, float r_center[3])
998 {
999         BMFace *efa;
1000         BMIter iter;
1001         uint center_accum_num = 0;
1002         zero_v3(r_center);
1003         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1004                 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1005                         float center[3];
1006                         BM_face_calc_center_mean(efa, center);
1007                         add_v3_v3(r_center, center);
1008                         center_accum_num += 1;
1009                 }
1010         }
1011         mul_v3_fl(r_center, 1.0f / (float)center_accum_num);
1012 }
1013
1014 static void uv_map_transform_center(
1015         Scene *scene, View3D *v3d, Object *ob, BMEditMesh *em,
1016         float r_center[3],
1017         float r_bounds[2][3])
1018 {
1019         /* only operates on the edit object - this is all that's needed now */
1020         const int around = (v3d) ? v3d->around : V3D_AROUND_CENTER_BOUNDS;
1021
1022         float bounds[2][3];
1023         INIT_MINMAX(bounds[0], bounds[1]);
1024         bool is_minmax_set = false;
1025
1026         switch (around) {
1027                 case V3D_AROUND_CENTER_BOUNDS: /* bounding box center */
1028                 {
1029                         uv_map_transform_calc_bounds(em, bounds[0], bounds[1]);
1030                         is_minmax_set = true;
1031                         mid_v3_v3v3(r_center, bounds[0], bounds[1]);
1032                         break;
1033                 }
1034                 case V3D_AROUND_CENTER_MEAN:
1035                 {
1036                         uv_map_transform_calc_center_median(em, r_center);
1037                         break;
1038                 }
1039                 case V3D_AROUND_CURSOR:  /* cursor center */
1040                 {
1041                         invert_m4_m4(ob->imat, ob->obmat);
1042                         mul_v3_m4v3(r_center, ob->imat, ED_view3d_cursor3d_get(scene, v3d)->location);
1043                         break;
1044                 }
1045                 case V3D_AROUND_ACTIVE:
1046                 {
1047                         BMEditSelection ese;
1048                         if (BM_select_history_active_get(em->bm, &ese)) {
1049                                 BM_editselection_center(&ese, r_center);
1050                                 break;
1051                         }
1052                         ATTR_FALLTHROUGH;
1053                 }
1054                 case V3D_AROUND_LOCAL_ORIGINS:  /* object center */
1055                 default:
1056                         zero_v3(r_center);
1057                         break;
1058         }
1059
1060         /* if this is passed, always set! */
1061         if (r_bounds) {
1062                 if (!is_minmax_set) {
1063                         uv_map_transform_calc_bounds(em, bounds[0], bounds[1]);
1064                 }
1065                 copy_v3_v3(r_bounds[0], bounds[0]);
1066                 copy_v3_v3(r_bounds[1], bounds[1]);
1067         }
1068 }
1069
1070 static void uv_map_rotation_matrix(float result[4][4], RegionView3D *rv3d, Object *ob,
1071                                    float upangledeg, float sideangledeg, float radius)
1072 {
1073         float rotup[4][4], rotside[4][4], viewmatrix[4][4], rotobj[4][4];
1074         float sideangle = 0.0f, upangle = 0.0f;
1075         int k;
1076
1077         /* get rotation of the current view matrix */
1078         if (rv3d)
1079                 copy_m4_m4(viewmatrix, rv3d->viewmat);
1080         else
1081                 unit_m4(viewmatrix);
1082
1083         /* but shifting */
1084         for (k = 0; k < 4; k++)
1085                 viewmatrix[3][k] = 0.0f;
1086
1087         /* get rotation of the current object matrix */
1088         copy_m4_m4(rotobj, ob->obmat);
1089
1090         /* but shifting */
1091         for (k = 0; k < 4; k++)
1092                 rotobj[3][k] = 0.0f;
1093
1094         zero_m4(rotup);
1095         zero_m4(rotside);
1096
1097         /* compensate front/side.. against opengl x,y,z world definition */
1098         /* this is "kanonen gegen spatzen", a few plus minus 1 will do here */
1099         /* i wanted to keep the reason here, so we're rotating*/
1100         sideangle = (float)M_PI * (sideangledeg + 180.0f) / 180.0f;
1101         rotside[0][0] =  cosf(sideangle);
1102         rotside[0][1] = -sinf(sideangle);
1103         rotside[1][0] =  sinf(sideangle);
1104         rotside[1][1] =  cosf(sideangle);
1105         rotside[2][2] =  1.0f;
1106
1107         upangle = (float)M_PI * upangledeg / 180.0f;
1108         rotup[1][1] =  cosf(upangle) / radius;
1109         rotup[1][2] = -sinf(upangle) / radius;
1110         rotup[2][1] =  sinf(upangle) / radius;
1111         rotup[2][2] =  cosf(upangle) / radius;
1112         rotup[0][0] =  1.0f / radius;
1113
1114         /* calculate transforms*/
1115         mul_m4_series(result, rotup, rotside, viewmatrix, rotobj);
1116 }
1117
1118 static void uv_map_transform(bContext *C, wmOperator *op, float rotmat[4][4])
1119 {
1120         /* context checks are messy here, making it work in both 3d view and uv editor */
1121         Object *obedit = CTX_data_edit_object(C);
1122         RegionView3D *rv3d = CTX_wm_region_view3d(C);
1123         /* common operator properties */
1124         int align = RNA_enum_get(op->ptr, "align");
1125         int direction = RNA_enum_get(op->ptr, "direction");
1126         float radius = RNA_struct_find_property(op->ptr, "radius") ? RNA_float_get(op->ptr, "radius") : 1.0f;
1127         float upangledeg, sideangledeg;
1128
1129         if (direction == VIEW_ON_EQUATOR) {
1130                 upangledeg = 90.0f;
1131                 sideangledeg = 0.0f;
1132         }
1133         else {
1134                 upangledeg = 0.0f;
1135                 if (align == POLAR_ZY) sideangledeg = 0.0f;
1136                 else sideangledeg = 90.0f;
1137         }
1138
1139         /* be compatible to the "old" sphere/cylinder mode */
1140         if (direction == ALIGN_TO_OBJECT)
1141                 unit_m4(rotmat);
1142         else 
1143                 uv_map_rotation_matrix(rotmat, rv3d, obedit, upangledeg, sideangledeg, radius);
1144
1145 }
1146
1147 static void uv_transform_properties(wmOperatorType *ot, int radius)
1148 {
1149         static const EnumPropertyItem direction_items[] = {
1150                 {VIEW_ON_EQUATOR, "VIEW_ON_EQUATOR", 0, "View on Equator", "3D view is on the equator"},
1151                 {VIEW_ON_POLES, "VIEW_ON_POLES", 0, "View on Poles", "3D view is on the poles"},
1152                 {ALIGN_TO_OBJECT, "ALIGN_TO_OBJECT", 0, "Align to Object", "Align according to object transform"},
1153                 {0, NULL, 0, NULL, NULL}
1154         };
1155         static const EnumPropertyItem align_items[] = {
1156                 {POLAR_ZX, "POLAR_ZX", 0, "Polar ZX", "Polar 0 is X"},
1157                 {POLAR_ZY, "POLAR_ZY", 0, "Polar ZY", "Polar 0 is Y"},
1158                 {0, NULL, 0, NULL, NULL}
1159         };
1160
1161         RNA_def_enum(ot->srna, "direction", direction_items, VIEW_ON_EQUATOR, "Direction",
1162                      "Direction of the sphere or cylinder");
1163         RNA_def_enum(ot->srna, "align", align_items, VIEW_ON_EQUATOR, "Align",
1164                      "How to determine rotation around the pole");
1165         if (radius)
1166                 RNA_def_float(ot->srna, "radius", 1.0f, 0.0f, FLT_MAX, "Radius",
1167                               "Radius of the sphere or cylinder", 0.0001f, 100.0f);
1168 }
1169
1170 static void correct_uv_aspect(Scene *scene, Object *ob, BMEditMesh *em)
1171 {
1172         BMLoop *l;
1173         BMIter iter, liter;
1174         MLoopUV *luv;
1175         BMFace *efa;
1176         float scale, aspx, aspy;
1177         
1178         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1179
1180         ED_uvedit_get_aspect(scene, ob, em->bm, &aspx, &aspy);
1181         
1182         if (aspx == aspy)
1183                 return;
1184                 
1185         if (aspx > aspy) {
1186                 scale = aspy / aspx;
1187
1188                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1189                         if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1190                                 continue;
1191                         
1192                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1193                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1194                                 luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f;
1195                         }
1196                 }
1197         }
1198         else {
1199                 scale = aspx / aspy;
1200
1201                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1202                         if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1203                                 continue;
1204                         
1205                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1206                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1207                                 luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f;
1208                         }
1209                 }
1210         }
1211 }
1212
1213 /******************** Map Clip & Correct ******************/
1214
1215 static void uv_map_clip_correct_properties(wmOperatorType *ot)
1216 {
1217         RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect",
1218                         "Map UVs taking image aspect ratio into account");
1219         RNA_def_boolean(ot->srna, "clip_to_bounds", 0, "Clip to Bounds",
1220                         "Clip UV coordinates to bounds after unwrapping");
1221         RNA_def_boolean(ot->srna, "scale_to_bounds", 0, "Scale to Bounds",
1222                         "Scale UV coordinates to bounds after unwrapping");
1223 }
1224
1225 static void uv_map_clip_correct(Scene *scene, Object *ob, BMEditMesh *em, wmOperator *op)
1226 {
1227         BMFace *efa;
1228         BMLoop *l;
1229         BMIter iter, liter;
1230         MLoopUV *luv;
1231         float dx, dy, min[2], max[2];
1232         const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
1233         const bool clip_to_bounds = RNA_boolean_get(op->ptr, "clip_to_bounds");
1234         const bool scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds");
1235
1236         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1237
1238         /* correct for image aspect ratio */
1239         if (correct_aspect)
1240                 correct_uv_aspect(scene, ob, em);
1241
1242         if (scale_to_bounds) {
1243                 INIT_MINMAX2(min, max);
1244
1245                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1246                         if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1247                                 continue;
1248
1249                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1250                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1251                                 minmax_v2v2_v2(min, max, luv->uv);
1252                         }
1253                 }
1254
1255                 /* rescale UV to be in 1/1 */
1256                 dx = (max[0] - min[0]);
1257                 dy = (max[1] - min[1]);
1258
1259                 if (dx > 0.0f)
1260                         dx = 1.0f / dx;
1261                 if (dy > 0.0f)
1262                         dy = 1.0f / dy;
1263
1264                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1265                         if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1266                                 continue;
1267
1268                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1269                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1270
1271                                 luv->uv[0] = (luv->uv[0] - min[0]) * dx;
1272                                 luv->uv[1] = (luv->uv[1] - min[1]) * dy;
1273                         }
1274                 }
1275         }
1276         else if (clip_to_bounds) {
1277                 /* clipping and wrapping */
1278                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1279                         if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1280                                 continue;
1281
1282                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1283                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1284                                 CLAMP(luv->uv[0], 0.0f, 1.0f);
1285                                 CLAMP(luv->uv[1], 0.0f, 1.0f);
1286                         }
1287                 }
1288         }
1289 }
1290
1291 /* ******************** Unwrap operator **************** */
1292
1293 /* assumes UV Map is checked, doesn't run update funcs */
1294 void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel, const bool pack)
1295 {
1296         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1297         ParamHandle *handle;
1298
1299         const bool fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0;
1300         const bool correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0;
1301         bool use_subsurf;
1302
1303         modifier_unwrap_state(obedit, scene, &use_subsurf);
1304
1305         if (use_subsurf)
1306                 handle = construct_param_handle_subsurfed(scene, obedit, em, fill_holes, sel, correct_aspect);
1307         else
1308                 handle = construct_param_handle(scene, obedit, em->bm, false, fill_holes, sel, correct_aspect);
1309
1310         param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0);
1311         param_lscm_solve(handle);
1312         param_lscm_end(handle);
1313
1314         param_average(handle);
1315
1316         if (pack) {
1317                 param_pack(handle, scene->toolsettings->uvcalc_margin, false);
1318         }
1319
1320         param_flush(handle);
1321
1322         param_delete(handle);
1323 }
1324
1325 static int unwrap_exec(bContext *C, wmOperator *op)
1326 {
1327         ViewLayer *view_layer = CTX_data_view_layer(C);
1328         Scene *scene = CTX_data_scene(C);
1329         int method = RNA_enum_get(op->ptr, "method");
1330         const bool fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
1331         const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
1332         const bool use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data");
1333         float obsize[3];
1334         bool implicit = false;
1335
1336         uint objects_len = 0;
1337         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
1338
1339         if (!uvedit_have_selection_multi(scene, objects, objects_len, implicit)) {
1340                 MEM_SAFE_FREE(objects);
1341                 return OPERATOR_CANCELLED;
1342         }
1343
1344         /* add uvs if they don't exist yet */
1345         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1346                 Object *obedit = objects[ob_index];
1347                 bool use_subsurf_final;
1348
1349                 if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1350                         continue;
1351                 }
1352
1353                 mat4_to_size(obsize, obedit->obmat);
1354                 if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f))
1355                         BKE_report(op->reports, RPT_INFO,
1356                                    "Object has non-uniform scale, unwrap will operate on a non-scaled version of the mesh");
1357                 else if (is_negative_m4(obedit->obmat))
1358                         BKE_report(op->reports, RPT_INFO,
1359                                    "Object has negative scale, unwrap will operate on a non-flipped version of the mesh");
1360
1361
1362                 /* double up the check here but better keep ED_unwrap_lscm interface simple and not
1363                  * pass operator for warning append */
1364                 modifier_unwrap_state(obedit, scene, &use_subsurf_final);
1365                 if (use_subsurf != use_subsurf_final) {
1366                         BKE_report(op->reports, RPT_INFO, "Subdivision Surface modifier needs to be first to work with unwrap");
1367                 }
1368         }
1369
1370         /* remember last method for live unwrap */
1371         if (RNA_struct_property_is_set(op->ptr, "method"))
1372                 scene->toolsettings->unwrapper = method;
1373         else
1374                 RNA_enum_set(op->ptr, "method", scene->toolsettings->unwrapper);
1375
1376         /* remember packing marging */
1377         if (RNA_struct_property_is_set(op->ptr, "margin"))
1378                 scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin");
1379         else
1380                 RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
1381
1382         if (fill_holes) scene->toolsettings->uvcalc_flag |=  UVCALC_FILLHOLES;
1383         else scene->toolsettings->uvcalc_flag &= ~UVCALC_FILLHOLES;
1384
1385         if (correct_aspect) scene->toolsettings->uvcalc_flag &= ~UVCALC_NO_ASPECT_CORRECT;
1386         else scene->toolsettings->uvcalc_flag |=  UVCALC_NO_ASPECT_CORRECT;
1387
1388         if (use_subsurf) scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF;
1389         else scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF;
1390
1391         /* execute unwrap */
1392         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1393                 Object *obedit = objects[ob_index];
1394                 ED_unwrap_lscm(scene, obedit, true, false);
1395                 DEG_id_tag_update(obedit->data, 0);
1396                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1397         }
1398
1399         ED_uvedit_pack_islands_multi(scene, objects, objects_len, true, true, true);
1400
1401         MEM_SAFE_FREE(objects);
1402
1403         return OPERATOR_FINISHED;
1404 }
1405
1406 void UV_OT_unwrap(wmOperatorType *ot)
1407 {
1408         static const EnumPropertyItem method_items[] = {
1409                 {0, "ANGLE_BASED", 0, "Angle Based", ""},
1410                 {1, "CONFORMAL", 0, "Conformal", ""},
1411                 {0, NULL, 0, NULL, NULL}
1412         };
1413
1414         /* identifiers */
1415         ot->name = "Unwrap";
1416         ot->description = "Unwrap the mesh of the object being edited";
1417         ot->idname = "UV_OT_unwrap";
1418         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1419         
1420         /* api callbacks */
1421         ot->exec = unwrap_exec;
1422         ot->poll = ED_operator_uvmap;
1423
1424         /* properties */
1425         RNA_def_enum(ot->srna, "method", method_items, 0, "Method",
1426                      "Unwrapping method (Angle Based usually gives better results than Conformal, while being somewhat slower)");
1427         RNA_def_boolean(ot->srna, "fill_holes", 1, "Fill Holes",
1428                         "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry");
1429         RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect",
1430                         "Map UVs taking image aspect ratio into account");
1431         RNA_def_boolean(ot->srna, "use_subsurf_data", 0, "Use Subsurf Modifier",
1432                         "Map UVs taking vertex position after Subdivision Surface modifier has been applied");
1433         RNA_def_float_factor(ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f);
1434 }
1435
1436 /**************** Project From View operator **************/
1437 static int uv_from_view_exec(bContext *C, wmOperator *op);
1438
1439 static int uv_from_view_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1440 {
1441         View3D *v3d = CTX_wm_view3d(C);
1442         RegionView3D *rv3d = CTX_wm_region_view3d(C);
1443         Camera *camera = ED_view3d_camera_data_get(v3d, rv3d);
1444         PropertyRNA *prop;
1445
1446         prop = RNA_struct_find_property(op->ptr, "camera_bounds");
1447         if (!RNA_property_is_set(op->ptr, prop)) RNA_property_boolean_set(op->ptr, prop, (camera != NULL));
1448         prop = RNA_struct_find_property(op->ptr, "correct_aspect");
1449         if (!RNA_property_is_set(op->ptr, prop)) RNA_property_boolean_set(op->ptr, prop, (camera == NULL));
1450
1451         return uv_from_view_exec(C, op);
1452 }
1453
1454 static int uv_from_view_exec(bContext *C, wmOperator *op)
1455 {
1456         ViewLayer *view_layer = CTX_data_view_layer(C);
1457         Scene *scene = CTX_data_scene(C);
1458         ARegion *ar = CTX_wm_region(C);
1459         View3D *v3d = CTX_wm_view3d(C);
1460         RegionView3D *rv3d = CTX_wm_region_view3d(C);
1461         Camera *camera = ED_view3d_camera_data_get(v3d, rv3d);
1462         BMFace *efa;
1463         BMLoop *l;
1464         BMIter iter, liter;
1465         MLoopUV *luv;
1466         float rotmat[4][4];
1467         bool changed_multi = false;
1468
1469         uint objects_len = 0;
1470         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
1471
1472         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1473                 Object *obedit = objects[ob_index];
1474                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1475                 bool changed = false;
1476
1477                 /* add uvs if they don't exist yet */
1478                 if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1479                         continue;
1480                 }
1481
1482                 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1483
1484                 if (RNA_boolean_get(op->ptr, "orthographic")) {
1485                         uv_map_rotation_matrix(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f);
1486
1487                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1488                                 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1489                                         continue;
1490
1491                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1492                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1493                                         BLI_uvproject_from_view_ortho(luv->uv, l->v->co, rotmat);
1494                                 }
1495                                 changed = true;
1496                         }
1497                 }
1498                 else if (camera) {
1499                         const bool camera_bounds = RNA_boolean_get(op->ptr, "camera_bounds");
1500                         struct ProjCameraInfo *uci = BLI_uvproject_camera_info(
1501                                 v3d->camera, obedit->obmat,
1502                                 camera_bounds ? (scene->r.xsch * scene->r.xasp) : 1.0f,
1503                                 camera_bounds ? (scene->r.ysch * scene->r.yasp) : 1.0f);
1504
1505                         if (uci) {
1506                                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1507                                         if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1508                                                 continue;
1509
1510                                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1511                                                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1512                                                 BLI_uvproject_from_camera(luv->uv, l->v->co, uci);
1513                                         }
1514                                         changed = true;
1515                                 }
1516
1517                                 MEM_freeN(uci);
1518                         }
1519                 }
1520                 else {
1521                         copy_m4_m4(rotmat, obedit->obmat);
1522
1523                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1524                                 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1525                                         continue;
1526
1527                                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1528                                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1529                                         BLI_uvproject_from_view(luv->uv, l->v->co, rv3d->persmat, rotmat, ar->winx, ar->winy);
1530                                 }
1531                                 changed = true;
1532                         }
1533                 }
1534
1535                 if (changed) {
1536                         changed_multi = true;
1537                         uv_map_clip_correct(scene, obedit, em, op);
1538
1539                         DEG_id_tag_update(obedit->data, 0);
1540                         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1541                 }
1542         }
1543         MEM_SAFE_FREE(objects);
1544
1545         if (changed_multi) {
1546                 return OPERATOR_FINISHED;
1547         }
1548         else {
1549                 return OPERATOR_CANCELLED;
1550         }
1551 }
1552
1553 static int uv_from_view_poll(bContext *C)
1554 {
1555         RegionView3D *rv3d = CTX_wm_region_view3d(C);
1556
1557         if (!ED_operator_uvmap(C))
1558                 return 0;
1559
1560         return (rv3d != NULL);
1561 }
1562
1563 void UV_OT_project_from_view(wmOperatorType *ot)
1564 {
1565         /* identifiers */
1566         ot->name = "Project From View";
1567         ot->idname = "UV_OT_project_from_view";
1568         ot->description = "Project the UV vertices of the mesh as seen in current 3D view";
1569
1570         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1571         
1572         /* api callbacks */
1573         ot->invoke = uv_from_view_invoke;
1574         ot->exec = uv_from_view_exec;
1575         ot->poll = uv_from_view_poll;
1576
1577         /* properties */
1578         RNA_def_boolean(ot->srna, "orthographic", 0, "Orthographic",
1579                         "Use orthographic projection");
1580         RNA_def_boolean(ot->srna, "camera_bounds", 1, "Camera Bounds",
1581                         "Map UVs to the camera region taking resolution and aspect into account");
1582         uv_map_clip_correct_properties(ot);
1583 }
1584
1585 /********************** Reset operator ********************/
1586
1587 static int reset_exec(bContext *C, wmOperator *UNUSED(op))
1588 {
1589         Scene *scene = CTX_data_scene(C);
1590         Object *obedit = CTX_data_edit_object(C);
1591         Mesh *me = (Mesh *)obedit->data;
1592
1593         /* add uvs if they don't exist yet */
1594         if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1595                 return OPERATOR_CANCELLED;
1596         }
1597
1598         ED_mesh_uv_loop_reset(C, me);
1599
1600         DEG_id_tag_update(obedit->data, 0);
1601         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1602         
1603         return OPERATOR_FINISHED;
1604 }
1605
1606 void UV_OT_reset(wmOperatorType *ot)
1607 {
1608         /* identifiers */
1609         ot->name = "Reset";
1610         ot->idname = "UV_OT_reset";
1611         ot->description = "Reset UV projection";
1612
1613         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1614         
1615         /* api callbacks */
1616         ot->exec = reset_exec;
1617         ot->poll = ED_operator_uvmap;
1618 }
1619
1620 /****************** Sphere Project operator ***************/
1621
1622 static void uv_sphere_project(float target[2], float source[3], float center[3], float rotmat[4][4])
1623 {
1624         float pv[3];
1625
1626         sub_v3_v3v3(pv, source, center);
1627         mul_m4_v3(rotmat, pv);
1628
1629         map_to_sphere(&target[0], &target[1], pv[0], pv[1], pv[2]);
1630
1631         /* split line is always zero */
1632         if (target[0] >= 1.0f)
1633                 target[0] -= 1.0f;  
1634 }
1635
1636 static void uv_map_mirror(BMEditMesh *em, BMFace *efa)
1637 {
1638         BMLoop *l;
1639         BMIter liter;
1640         MLoopUV *luv;
1641         float **uvs = BLI_array_alloca(uvs, efa->len);
1642         float dx;
1643         int i, mi;
1644
1645         const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1646
1647         BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1648                 luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1649                 uvs[i] = luv->uv;
1650         }
1651
1652         mi = 0;
1653         for (i = 1; i < efa->len; i++)
1654                 if (uvs[i][0] > uvs[mi][0])
1655                         mi = i;
1656
1657         for (i = 0; i < efa->len; i++) {
1658                 if (i != mi) {
1659                         dx = uvs[mi][0] - uvs[i][0];
1660                         if (dx > 0.5f) uvs[i][0] += 1.0f;
1661                 }
1662         }
1663 }
1664
1665 static int sphere_project_exec(bContext *C, wmOperator *op)
1666 {
1667         Scene *scene = CTX_data_scene(C);
1668         Object *obedit = CTX_data_edit_object(C);
1669         View3D *v3d = CTX_wm_view3d(C);
1670         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1671         BMFace *efa;
1672         BMLoop *l;
1673         BMIter iter, liter;
1674         MLoopUV *luv;
1675         float center[3], rotmat[4][4];
1676
1677         int cd_loop_uv_offset;
1678
1679         /* add uvs if they don't exist yet */
1680         if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1681                 return OPERATOR_CANCELLED;
1682         }
1683
1684         cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1685
1686         uv_map_transform(C, op, rotmat);
1687         uv_map_transform_center(scene, v3d, obedit, em, center, NULL);
1688
1689         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1690                 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1691                         continue;
1692
1693                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1694                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1695
1696                         uv_sphere_project(luv->uv, l->v->co, center, rotmat);
1697                 }
1698
1699                 uv_map_mirror(em, efa);
1700         }
1701
1702         uv_map_clip_correct(scene, obedit, em, op);
1703
1704         DEG_id_tag_update(obedit->data, 0);
1705         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1706
1707         return OPERATOR_FINISHED;
1708 }
1709
1710 void UV_OT_sphere_project(wmOperatorType *ot)
1711 {
1712         /* identifiers */
1713         ot->name = "Sphere Projection";
1714         ot->idname = "UV_OT_sphere_project";
1715         ot->description = "Project the UV vertices of the mesh over the curved surface of a sphere";
1716
1717         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1718         
1719         /* api callbacks */
1720         ot->exec = sphere_project_exec;
1721         ot->poll = ED_operator_uvmap;
1722
1723         /* properties */
1724         uv_transform_properties(ot, 0);
1725         uv_map_clip_correct_properties(ot);
1726 }
1727
1728 /***************** Cylinder Project operator **************/
1729
1730 static void uv_cylinder_project(float target[2], float source[3], float center[3], float rotmat[4][4])
1731 {
1732         float pv[3];
1733
1734         sub_v3_v3v3(pv, source, center);
1735         mul_m4_v3(rotmat, pv);
1736
1737         map_to_tube(&target[0], &target[1], pv[0], pv[1], pv[2]);
1738
1739         /* split line is always zero */
1740         if (target[0] >= 1.0f)
1741                 target[0] -= 1.0f;  
1742 }
1743
1744 static int cylinder_project_exec(bContext *C, wmOperator *op)
1745 {
1746         Scene *scene = CTX_data_scene(C);
1747         Object *obedit = CTX_data_edit_object(C);
1748         View3D *v3d = CTX_wm_view3d(C);
1749         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1750         BMFace *efa;
1751         BMLoop *l;
1752         BMIter iter, liter;
1753         MLoopUV *luv;
1754         float center[3], rotmat[4][4];
1755
1756         int cd_loop_uv_offset;
1757
1758         /* add uvs if they don't exist yet */
1759         if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1760                 return OPERATOR_CANCELLED;
1761         }
1762
1763         cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1764
1765         uv_map_transform(C, op, rotmat);
1766         uv_map_transform_center(scene, v3d, obedit, em, center, NULL);
1767
1768         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1769                 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
1770                         continue;
1771
1772                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1773                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1774
1775                         uv_cylinder_project(luv->uv, l->v->co, center, rotmat);
1776                 }
1777
1778                 uv_map_mirror(em, efa);
1779         }
1780
1781         uv_map_clip_correct(scene, obedit, em, op);
1782
1783         DEG_id_tag_update(obedit->data, 0);
1784         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1785
1786         return OPERATOR_FINISHED;
1787 }
1788
1789 void UV_OT_cylinder_project(wmOperatorType *ot)
1790 {
1791         /* identifiers */
1792         ot->name = "Cylinder Projection";
1793         ot->idname = "UV_OT_cylinder_project";
1794         ot->description = "Project the UV vertices of the mesh over the curved wall of a cylinder";
1795
1796         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1797         
1798         /* api callbacks */
1799         ot->exec = cylinder_project_exec;
1800         ot->poll = ED_operator_uvmap;
1801
1802         /* properties */
1803         uv_transform_properties(ot, 1);
1804         uv_map_clip_correct_properties(ot);
1805 }
1806
1807 /******************* Cube Project operator ****************/
1808
1809 void ED_uvedit_unwrap_cube_project(BMesh *bm, float cube_size, bool use_select, const float center[3])
1810 {
1811         BMFace *efa;
1812         BMLoop *l;
1813         BMIter iter, liter;
1814         MLoopUV *luv;
1815         float loc[3];
1816         int cox, coy;
1817
1818         int cd_loop_uv_offset;
1819
1820         cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
1821
1822         if (center) {
1823                 copy_v3_v3(loc, center);
1824         }
1825         else {
1826                 zero_v3(loc);
1827         }
1828
1829         /* choose x,y,z axis for projection depending on the largest normal
1830          * component, but clusters all together around the center of map. */
1831
1832         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1833                 /* tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); */ /* UNUSED */
1834                 if (use_select && !BM_elem_flag_test(efa, BM_ELEM_SELECT))
1835                         continue;
1836
1837                 axis_dominant_v3(&cox, &coy, efa->no);
1838
1839                 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1840                         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1841                         luv->uv[0] = 0.5f + 0.5f * cube_size * (l->v->co[cox] - loc[cox]);
1842                         luv->uv[1] = 0.5f + 0.5f * cube_size * (l->v->co[coy] - loc[coy]);
1843                 }
1844         }
1845 }
1846
1847 static int cube_project_exec(bContext *C, wmOperator *op)
1848 {
1849         Scene *scene = CTX_data_scene(C);
1850         View3D *v3d = CTX_wm_view3d(C);
1851         Object *obedit = CTX_data_edit_object(C);
1852         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1853         PropertyRNA *prop_cube_size = RNA_struct_find_property(op->ptr, "cube_size");
1854         float cube_size = RNA_property_float_get(op->ptr, prop_cube_size);
1855         float center[3];
1856         float bounds[2][3];
1857         float (*bounds_buf)[3] = NULL;
1858
1859         /* add uvs if they don't exist yet */
1860         if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1861                 return OPERATOR_CANCELLED;
1862         }
1863
1864         if (!RNA_property_is_set(op->ptr, prop_cube_size)) {
1865                 bounds_buf = bounds;
1866         }
1867
1868         uv_map_transform_center(scene, v3d, obedit, em, center, bounds_buf);
1869
1870         /* calculate based on bounds */
1871         if (bounds_buf) {
1872                 float dims[3];
1873                 sub_v3_v3v3(dims, bounds[1], bounds[0]);
1874                 cube_size = max_fff(UNPACK3(dims));
1875                 cube_size = cube_size ? 2.0f / cube_size : 1.0f;
1876                 RNA_property_float_set(op->ptr, prop_cube_size, cube_size);
1877         }
1878
1879         ED_uvedit_unwrap_cube_project(em->bm, cube_size, true, center);
1880
1881         uv_map_clip_correct(scene, obedit, em, op);
1882
1883         DEG_id_tag_update(obedit->data, 0);
1884         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1885
1886         return OPERATOR_FINISHED;
1887 }
1888
1889 void UV_OT_cube_project(wmOperatorType *ot)
1890 {
1891         /* identifiers */
1892         ot->name = "Cube Projection";
1893         ot->idname = "UV_OT_cube_project";
1894         ot->description = "Project the UV vertices of the mesh over the six faces of a cube";
1895
1896         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1897         
1898         /* api callbacks */
1899         ot->exec = cube_project_exec;
1900         ot->poll = ED_operator_uvmap;
1901
1902         /* properties */
1903         RNA_def_float(ot->srna, "cube_size", 1.0f, 0.0f, FLT_MAX, "Cube Size", "Size of the cube to project on", 0.001f, 100.0f);
1904         uv_map_clip_correct_properties(ot);
1905 }