part 1 of cleaning up my little array macro library to be a formal API. also removed...
[blender.git] / source / blender / editors / uvedit / uvedit_unwrap_ops.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <string.h>
31 #include <stdlib.h>
32 #include <math.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_mesh_types.h"
37 #include "DNA_meshdata_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
40 #include "DNA_screen_types.h"
41 #include "DNA_space_types.h"
42
43 #include "BKE_context.h"
44 #include "BKE_customdata.h"
45 #include "BKE_depsgraph.h"
46 #include "BKE_global.h"
47 #include "BKE_image.h"
48 #include "BKE_mesh.h"
49 #include "BKE_utildefines.h"
50 #include "BKE_tessmesh.h"
51
52 #include "BLI_arithb.h"
53 #include "BLI_edgehash.h"
54 #include "BLI_editVert.h"
55 #include "BLI_scanfill.h"
56 #include "BLI_array.h"
57
58 #include "PIL_time.h"
59
60 #include "ED_image.h"
61 #include "ED_mesh.h"
62 #include "ED_screen.h"
63 #include "ED_uvedit.h"
64 #include "ED_view3d.h"
65
66 #include "RNA_access.h"
67 #include "RNA_define.h"
68
69 #include "UI_interface.h"
70
71 #include "WM_api.h"
72 #include "WM_types.h"
73
74 #include "uvedit_intern.h"
75 #include "uvedit_parametrizer.h"
76
77 static int ED_uvedit_ensure_uvs(bContext *C, Scene *scene, Object *obedit)
78 {
79         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
80         BMFace *efa;
81         BMIter iter;
82         Image *ima;
83         bScreen *sc;
84         ScrArea *sa;
85         SpaceLink *slink;
86         SpaceImage *sima;
87
88         if(ED_uvedit_test(obedit)) {
89                 return 1;
90         }
91
92         if(em && em->bm->totface) {// && !CustomData_has_layer(&em->bm->pdata, CD_MTEXPOLY)) {
93                 BM_add_data_layer(em->bm, &em->bm->pdata, CD_MTEXPOLY);
94                 BM_add_data_layer(em->bm, &em->bm->ldata, CD_MLOOPUV);
95         }
96
97         if(!ED_uvedit_test(obedit)) {
98                 return 0;
99         }
100
101         ima= CTX_data_edit_image(C);
102
103         if(!ima) {
104                 /* no image in context in the 3d view, we find first image window .. */
105                 sc= CTX_wm_screen(C);
106
107                 for(sa=sc->areabase.first; sa; sa=sa->next) {
108                         slink= sa->spacedata.first;
109                         if(slink->spacetype == SPACE_IMAGE) {
110                                 sima= (SpaceImage*)slink;
111
112                                 ima= sima->image;
113                                 if(ima) {
114                                         if(ima->type==IMA_TYPE_R_RESULT || ima->type==IMA_TYPE_COMPOSITE)
115                                                 ima= NULL;
116                                         else
117                                                 break;
118                                 }
119                         }
120                 }
121         }
122         
123         if(ima)
124                 ED_uvedit_assign_image(scene, obedit, ima, NULL);
125         
126         /* select new UV's */
127         BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
128                 uvedit_face_select(scene, em, efa);
129         }
130
131         return 1;
132 }
133
134 /****************** Parametrizer Conversion ***************/
135
136 ParamHandle *construct_param_handle(Scene *scene, BMEditMesh *em, 
137                                     short implicit, short fill, short sel, 
138                                     short correct_aspect)
139 {
140         ParamHandle *handle;
141         BMFace *efa;
142         BMLoop *l;
143         BMEdge *eed;
144         BMVert *ev;
145         BMIter iter, liter;
146         MTexPoly *tf;
147         int a;
148         
149         handle = param_construct_begin();
150
151         if(correct_aspect) {
152                 efa = EDBM_get_actFace(em, 1);
153
154                 if(efa) {
155                         MTexPoly *tf= CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
156                         float aspx, aspy;
157
158                         ED_image_uv_aspect(tf->tpage, &aspx, &aspy);
159                 
160                         if(aspx!=aspy)
161                                 param_aspect_ratio(handle, aspx, aspy);
162                 }
163         }
164         
165         /* we need the vert indicies */
166         a = 0;
167         BM_ITER(ev, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
168                 BMINDEX_SET(ev, a);
169                 a++;
170         }
171         
172         BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
173                 EditVert *v, *lastv, *firstv;
174                 EditFace *sefa;
175                 ParamKey key, vkeys[4];
176                 ParamBool pin[4], select[4];
177                 BMLoop *ls[3];
178                 MLoopUV *luvs[3];
179                 float *co[4];
180                 float *uv[4];
181                 int lsel;
182                 
183                 if((BM_TestHFlag(efa, BM_HIDDEN)) || (sel && BM_TestHFlag(efa, BM_SELECT)==0)) 
184                         continue;
185
186                 tf= (MTexPoly *)CustomData_em_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
187                 lsel = 0;
188
189                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
190                         if (uvedit_uv_selected(em, scene, l)) {
191                                 lsel = 1;
192                                 break;
193                         }
194                 }
195
196                 if (implicit && !lsel)
197                         continue;
198
199                 key = (ParamKey)efa;
200
201                 /*scanfill time!*/
202                 firstv = lastv = NULL;
203                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
204                         v = BLI_addfillvert(l->v->co);
205                         
206                         v->tmp.p = l;
207
208                         if (lastv) {
209                                 BLI_addfilledge(lastv, v);
210                         }
211
212                         lastv = v;
213                         if (!firstv) 
214                                 firstv = v;
215                 }
216
217                 BLI_addfilledge(firstv, v);
218                 
219                 /*mode 2 enables shortest-diagonal for quads*/
220                 BLI_edgefill(2, 0);
221                 for (sefa = fillfacebase.first; sefa; sefa=sefa->next) {
222                         ls[0] = sefa->v1->tmp.p;
223                         ls[1] = sefa->v2->tmp.p;
224                         ls[2] = sefa->v3->tmp.p;
225                         
226                         luvs[0] = CustomData_bmesh_get(&em->bm->ldata, ls[0]->head.data, CD_MLOOPUV);
227                         luvs[1] = CustomData_bmesh_get(&em->bm->ldata, ls[1]->head.data, CD_MLOOPUV);
228                         luvs[2] = CustomData_bmesh_get(&em->bm->ldata, ls[2]->head.data, CD_MLOOPUV);
229
230                         vkeys[0] = (ParamKey)BMINDEX_GET(ls[0]->v);
231                         vkeys[1] = (ParamKey)BMINDEX_GET(ls[1]->v);
232                         vkeys[2] = (ParamKey)BMINDEX_GET(ls[2]->v);
233
234                         co[0] = ls[0]->v->co;
235                         co[1] = ls[1]->v->co;
236                         co[2] = ls[2]->v->co;
237
238                         uv[0] = luvs[0]->uv;
239                         uv[1] = luvs[1]->uv;
240                         uv[2] = luvs[2]->uv;
241
242                         pin[0] = (luvs[0]->flag & MLOOPUV_PINNED) != 0;
243                         pin[1] = (luvs[1]->flag & MLOOPUV_PINNED) != 0;
244                         pin[2] = (luvs[2]->flag & MLOOPUV_PINNED) != 0;
245
246                         select[0] = uvedit_uv_selected(em, scene, ls[0]) != 0;
247                         select[1] = uvedit_uv_selected(em, scene, ls[1]) != 0;
248                         select[2] = uvedit_uv_selected(em, scene, ls[2]) != 0;
249
250                         if (!p_face_exists(handle,vkeys,0,1,2))
251                                         param_face_add(handle, key, 3, vkeys, co, uv, pin, select);
252                 }
253
254                 BLI_end_edgefill();
255         }
256
257         if(!implicit) {
258                 BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
259                         if(BM_TestHFlag(eed, BM_SEAM)) {
260                                 ParamKey vkeys[2];
261                                 vkeys[0] = (ParamKey)BMINDEX_GET(eed->v1);
262                                 vkeys[1] = (ParamKey)BMINDEX_GET(eed->v2);
263                                 param_edge_set_seam(handle, vkeys);
264                         }
265                 }
266         }
267
268         param_construct_end(handle, fill, implicit);
269
270         return handle;
271 }
272
273 /* ******************** Minimize Stretch operator **************** */
274
275 typedef struct MinStretch {
276         Scene *scene;
277         Object *obedit;
278         BMEditMesh *em;
279         ParamHandle *handle;
280         float blend;
281         double lasttime;
282         int i, iterations;
283         wmTimer *timer;
284 } MinStretch;
285
286 static void minimize_stretch_init(bContext *C, wmOperator *op)
287 {
288         Scene *scene= CTX_data_scene(C);
289         Object *obedit= CTX_data_edit_object(C);
290         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
291         MinStretch *ms;
292         int fill_holes= RNA_boolean_get(op->ptr, "fill_holes");
293
294         ms= MEM_callocN(sizeof(MinStretch), "MinStretch");
295         ms->scene= scene;
296         ms->obedit= obedit;
297         ms->em= em;
298         ms->blend= RNA_float_get(op->ptr, "blend");
299         ms->iterations= RNA_int_get(op->ptr, "iterations");
300         ms->handle= construct_param_handle(scene, em, 1, fill_holes, 1, 1);
301         ms->lasttime= PIL_check_seconds_timer();
302
303         param_stretch_begin(ms->handle);
304         if(ms->blend != 0.0f)
305                 param_stretch_blend(ms->handle, ms->blend);
306
307         op->customdata= ms;
308 }
309
310 static void minimize_stretch_iteration(bContext *C, wmOperator *op, int interactive)
311 {
312         MinStretch *ms= op->customdata;
313         ScrArea *sa= CTX_wm_area(C);
314
315         param_stretch_blend(ms->handle, ms->blend);
316         param_stretch_iter(ms->handle);
317
318         if(interactive && (PIL_check_seconds_timer() - ms->lasttime > 0.5)) {
319                 char str[100];
320
321                 param_flush(ms->handle);
322
323                 if(sa) {
324                         sprintf(str, "Minimize Stretch. Blend %.2f.", ms->blend);
325                         ED_area_headerprint(sa, str);
326                 }
327
328                 ms->lasttime = PIL_check_seconds_timer();
329
330                 DAG_id_flush_update(ms->obedit->data, OB_RECALC_DATA);
331                 WM_event_add_notifier(C, NC_GEOM|ND_DATA, ms->obedit->data);
332         }
333 }
334
335 static void minimize_stretch_exit(bContext *C, wmOperator *op, int cancel)
336 {
337         MinStretch *ms= op->customdata;
338         ScrArea *sa= CTX_wm_area(C);
339
340         if(sa)
341                 ED_area_headerprint(sa, NULL);
342         if(ms->timer)
343                 WM_event_remove_window_timer(CTX_wm_window(C), ms->timer);
344
345         if(cancel)
346                 param_flush_restore(ms->handle);
347         else
348                 param_flush(ms->handle);
349
350         param_stretch_end(ms->handle);
351         param_delete(ms->handle);
352
353         DAG_id_flush_update(ms->obedit->data, OB_RECALC_DATA);
354         WM_event_add_notifier(C, NC_GEOM|ND_DATA, ms->obedit->data);
355
356         MEM_freeN(ms);
357         op->customdata= NULL;
358 }
359
360 static int minimize_stretch_exec(bContext *C, wmOperator *op)
361 {
362         int i, iterations;
363
364         minimize_stretch_init(C, op);
365
366         iterations= RNA_int_get(op->ptr, "iterations");
367         for(i=0; i<iterations; i++)
368                 minimize_stretch_iteration(C, op, 0);
369         minimize_stretch_exit(C, op, 0);
370
371         return OPERATOR_FINISHED;
372 }
373
374 static int minimize_stretch_invoke(bContext *C, wmOperator *op, wmEvent *event)
375 {
376         MinStretch *ms;
377
378         minimize_stretch_init(C, op);
379         minimize_stretch_iteration(C, op, 1);
380
381         ms= op->customdata;
382         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
383         ms->timer= WM_event_add_window_timer(CTX_wm_window(C), TIMER, 0.01f);
384
385         return OPERATOR_RUNNING_MODAL;
386 }
387
388 static int minimize_stretch_modal(bContext *C, wmOperator *op, wmEvent *event)
389 {
390         MinStretch *ms= op->customdata;
391
392         switch(event->type) {
393                 case ESCKEY:
394                 case RIGHTMOUSE:
395                         minimize_stretch_exit(C, op, 1);
396                         return OPERATOR_CANCELLED;
397                 case RETKEY:
398                 case PADENTER:
399                 case LEFTMOUSE:
400                         minimize_stretch_exit(C, op, 0);
401                         return OPERATOR_FINISHED;
402                 case PADPLUSKEY:
403                 case WHEELUPMOUSE:
404                         if(ms->blend < 0.95f) {
405                                 ms->blend += 0.1f;
406                                 ms->lasttime= 0.0f;
407                                 RNA_float_set(op->ptr, "blend", ms->blend);
408                                 minimize_stretch_iteration(C, op, 1);
409                         }
410                         break;
411                 case PADMINUS:
412                 case WHEELDOWNMOUSE:
413                         if(ms->blend > 0.05f) {
414                                 ms->blend -= 0.1f;
415                                 ms->lasttime= 0.0f;
416                                 RNA_float_set(op->ptr, "blend", ms->blend);
417                                 minimize_stretch_iteration(C, op, 1);
418                         }
419                         break;
420                 case TIMER:
421                         if(ms->timer == event->customdata) {
422                                 double start= PIL_check_seconds_timer();
423
424                                 do {
425                                         minimize_stretch_iteration(C, op, 1);
426                                 } while(PIL_check_seconds_timer() - start < 0.01);
427                         }
428                         break;
429         }
430
431         if(ms->iterations && ms->i >= ms->iterations) {
432                 minimize_stretch_exit(C, op, 0);
433                 return OPERATOR_FINISHED;
434         }
435
436         return OPERATOR_RUNNING_MODAL;
437 }
438
439 static int minimize_stretch_cancel(bContext *C, wmOperator *op)
440 {
441         minimize_stretch_exit(C, op, 1);
442
443         return OPERATOR_CANCELLED;
444 }
445
446 void UV_OT_minimize_stretch(wmOperatorType *ot)
447 {
448         /* identifiers */
449         ot->name= "Minimize Stretch";
450         ot->idname= "UV_OT_minimize_stretch";
451         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
452         ot->description="DOC_BROKEN";
453         
454         /* api callbacks */
455         ot->exec= minimize_stretch_exec;
456         ot->invoke= minimize_stretch_invoke;
457         ot->modal= minimize_stretch_modal;
458         ot->cancel= minimize_stretch_cancel;
459         ot->poll= ED_operator_uvedit;
460
461         /* properties */
462         RNA_def_boolean(ot->srna, "fill_holes", 1, "Fill Holes", "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry.");
463         RNA_def_float_percentage(ot->srna, "blend", 0.0f, 0.0f, 1.0f, "Blend", "Blend factor between stretch minimized and original.", 0.0f, 1.0f);
464         RNA_def_int(ot->srna, "iterations", 0, 0, INT_MAX, "Iterations", "Number of iterations to run, 0 is unlimited when run interactively.", 0, 100);
465 }
466
467 /* ******************** Pack Islands operator **************** */
468
469 static int pack_islands_exec(bContext *C, wmOperator *op)
470 {
471         Scene *scene= CTX_data_scene(C);
472         Object *obedit= CTX_data_edit_object(C);
473         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
474         ParamHandle *handle;
475
476         handle = construct_param_handle(scene, em, 1, 0, 1, 1);
477         param_pack(handle, scene->toolsettings->uvcalc_margin);
478         param_flush(handle);
479         param_delete(handle);
480         
481         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
482         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
483
484         return OPERATOR_FINISHED;
485 }
486
487 void UV_OT_pack_islands(wmOperatorType *ot)
488 {
489         /* identifiers */
490         ot->name= "Pack Islands";
491         ot->idname= "UV_OT_pack_islands";
492         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
493         
494         /* api callbacks */
495         ot->exec= pack_islands_exec;
496         ot->poll= ED_operator_uvedit;
497 }
498
499 /* ******************** Average Islands Scale operator **************** */
500
501 static int average_islands_scale_exec(bContext *C, wmOperator *op)
502 {
503         Scene *scene= CTX_data_scene(C);
504         Object *obedit= CTX_data_edit_object(C);
505         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
506         ParamHandle *handle;
507
508         handle= construct_param_handle(scene, em, 1, 0, 1, 1);
509         param_average(handle);
510         param_flush(handle);
511         param_delete(handle);
512         
513         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
514         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
515
516         return OPERATOR_FINISHED;
517 }
518
519 void UV_OT_average_islands_scale(wmOperatorType *ot)
520 {
521         /* identifiers */
522         ot->name= "Average Islands Scale";
523         ot->idname= "UV_OT_average_islands_scale";
524         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
525         
526         /* api callbacks */
527         ot->exec= average_islands_scale_exec;
528         ot->poll= ED_operator_uvedit;
529 }
530
531 /**************** Live Unwrap *****************/
532
533 static ParamHandle *liveHandle = NULL;
534
535 void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
536 {
537         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
538         short abf = scene->toolsettings->unwrapper == 1;
539         short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES;
540
541         if(!ED_uvedit_test(obedit)) {
542                 return;
543         }
544
545         liveHandle = construct_param_handle(scene, em, 0, fillholes, 0, 1);
546
547         param_lscm_begin(liveHandle, PARAM_TRUE, abf);
548 }
549
550 void ED_uvedit_live_unwrap_re_solve(void)
551 {
552         if(liveHandle) {
553                 param_lscm_solve(liveHandle);
554                 param_flush(liveHandle);
555         }
556 }
557         
558 void ED_uvedit_live_unwrap_end(short cancel)
559 {
560         if(liveHandle) {
561                 param_lscm_end(liveHandle);
562                 if(cancel)
563                         param_flush_restore(liveHandle);
564                 param_delete(liveHandle);
565                 liveHandle = NULL;
566         }
567 }
568
569 /*************** UV Map Common Transforms *****************/
570
571 #define VIEW_ON_EQUATOR 0
572 #define VIEW_ON_POLES   1
573 #define ALIGN_TO_OBJECT 2
574
575 #define POLAR_ZX        0
576 #define POLAR_ZY        1
577
578 static void uv_map_transform_center(Scene *scene, View3D *v3d, float *result, 
579                                     Object *ob, BMEditMesh *em)
580 {
581         BMFace *efa;
582         BMLoop *l;
583         BMIter iter, liter;
584         float min[3], max[3], *cursx;
585         int around= (v3d)? v3d->around: V3D_CENTER;
586
587         /* only operates on the edit object - this is all that's needed now */
588
589         switch(around)  {
590                 case V3D_CENTER: /* bounding box center */
591                         min[0]= min[1]= min[2]= 1e20f;
592                         max[0]= max[1]= max[2]= -1e20f; 
593                         
594                         BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL)  {
595                                 if(BM_TestHFlag(efa, BM_SELECT)) {
596                                         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
597                                                 DO_MINMAX(l->v->co, min, max);
598                                         }
599                                 }
600                         }
601                         VecMidf(result, min, max);
602                         break;
603
604                 case V3D_CURSOR: /*cursor center*/ 
605                         cursx= give_cursor(scene, v3d);
606                         /* shift to objects world */
607                         result[0]= cursx[0]-ob->obmat[3][0];
608                         result[1]= cursx[1]-ob->obmat[3][1];
609                         result[2]= cursx[2]-ob->obmat[3][2];
610                         break;
611
612                 case V3D_LOCAL: /*object center*/
613                 case V3D_CENTROID: /* multiple objects centers, only one object here*/
614                 default:
615                         result[0]= result[1]= result[2]= 0.0;
616                         break;
617         }
618 }
619
620 static void uv_map_rotation_matrix(float result[][4], RegionView3D *rv3d, Object *ob, float upangledeg, float sideangledeg, float radius)
621 {
622         float rotup[4][4], rotside[4][4], viewmatrix[4][4], rotobj[4][4];
623         float sideangle= 0.0f, upangle= 0.0f;
624         int k;
625
626         /* get rotation of the current view matrix */
627         if(rv3d)
628                 Mat4CpyMat4(viewmatrix, rv3d->viewmat);
629         else
630                 Mat4One(viewmatrix);
631
632         /* but shifting */
633         for(k=0; k<4; k++)
634                 viewmatrix[3][k] =0.0f;
635
636         /* get rotation of the current object matrix */
637         Mat4CpyMat4(rotobj,ob->obmat);
638
639         /* but shifting */
640         for(k=0; k<4; k++)
641                 rotobj[3][k] =0.0f;
642
643         Mat4Clr(*rotup);
644         Mat4Clr(*rotside);
645
646         /* compensate front/side.. against opengl x,y,z world definition */
647         /* this is "kanonen gegen spatzen", a few plus minus 1 will do here */
648         /* i wanted to keep the reason here, so we're rotating*/
649         sideangle= (float)M_PI*(sideangledeg + 180.0f)/180.0f;
650         rotside[0][0]= (float)cos(sideangle);
651         rotside[0][1]= -(float)sin(sideangle);
652         rotside[1][0]= (float)sin(sideangle);
653         rotside[1][1]= (float)cos(sideangle);
654         rotside[2][2]= 1.0f;
655       
656         upangle= (float)M_PI*upangledeg/180.0f;
657         rotup[1][1]= (float)cos(upangle)/radius;
658         rotup[1][2]= -(float)sin(upangle)/radius;
659         rotup[2][1]= (float)sin(upangle)/radius;
660         rotup[2][2]= (float)cos(upangle)/radius;
661         rotup[0][0]= (float)1.0f/radius;
662
663         /* calculate transforms*/
664         Mat4MulSerie(result, rotup, rotside, viewmatrix, rotobj, NULL, NULL, NULL, NULL);
665 }
666
667 static void uv_map_transform(bContext *C, wmOperator *op, float center[3], float rotmat[4][4])
668 {
669         /* context checks are messy here, making it work in both 3d view and uv editor */
670         Scene *scene= CTX_data_scene(C);
671         Object *obedit= CTX_data_edit_object(C);
672         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
673         View3D *v3d= CTX_wm_view3d(C);
674         RegionView3D *rv3d= CTX_wm_region_view3d(C);
675         /* common operator properties */
676         int align= RNA_enum_get(op->ptr, "align");
677         int direction= RNA_enum_get(op->ptr, "direction");
678         float radius= RNA_struct_find_property(op->ptr, "radius")? RNA_float_get(op->ptr, "radius"): 1.0f;
679         float upangledeg, sideangledeg;
680
681         uv_map_transform_center(scene, v3d, center, obedit, em);
682
683         if(direction == VIEW_ON_EQUATOR) {
684                 upangledeg= 90.0f;
685                 sideangledeg= 0.0f;
686         }
687         else {
688                 upangledeg= 0.0f;
689                 if(align == POLAR_ZY) sideangledeg= 0.0f;
690                 else sideangledeg= 90.0f;
691         }
692
693         /* be compatible to the "old" sphere/cylinder mode */
694         if(direction == ALIGN_TO_OBJECT)
695                 Mat4One(rotmat);
696         else 
697                 uv_map_rotation_matrix(rotmat, rv3d, obedit, upangledeg, sideangledeg, radius);
698
699 }
700
701 static void uv_transform_properties(wmOperatorType *ot, int radius)
702 {
703         static EnumPropertyItem direction_items[]= {
704                 {VIEW_ON_EQUATOR, "VIEW_ON_EQUATOR", 0, "View on Equator", "3D view is on the equator."},
705                 {VIEW_ON_POLES, "VIEW_ON_POLES", 0, "View on Poles", "3D view is on the poles."},
706                 {ALIGN_TO_OBJECT, "ALIGN_TO_OBJECT", 0, "Align to Object", "Align according to object transform."},
707                 {0, NULL, 0, NULL, NULL}
708         };
709         static EnumPropertyItem align_items[]= {
710                 {POLAR_ZX, "POLAR_ZX", 0, "Polar ZX", "Polar 0 is X."},
711                 {POLAR_ZY, "POLAR_ZY", 0, "Polar ZY", "Polar 0 is Y."},
712                 {0, NULL, 0, NULL, NULL}
713         };
714
715         RNA_def_enum(ot->srna, "direction", direction_items, VIEW_ON_EQUATOR, "Direction", "Direction of the sphere or cylinder.");
716         RNA_def_enum(ot->srna, "align", align_items, VIEW_ON_EQUATOR, "Align", "How to determine rotation around the pole.");
717         if(radius)
718                 RNA_def_float(ot->srna, "radius", 1.0f, 0.0f, FLT_MAX, "Radius", "Radius of the sphere or cylinder.", 0.0001f, 100.0f);
719 }
720
721 static void correct_uv_aspect(BMEditMesh *em)
722 {
723         BMFace *efa= EDBM_get_actFace(em, 1);
724         BMLoop *l;
725         BMIter iter, liter;
726         MTexPoly *tf;
727         MLoopUV *luv;
728         float scale, aspx= 1.0f, aspy=1.0f;
729         
730         if(efa) {
731                 tf= CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
732                 ED_image_uv_aspect(tf->tpage, &aspx, &aspy);
733         }
734         
735         if(aspx == aspy)
736                 return;
737                 
738         if(aspx > aspy) {
739                 scale= aspy/aspx;
740
741                 BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
742                         tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
743                         if (!BM_TestHFlag(efa, BM_SELECT))
744                                 continue;
745                         
746                         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
747                                 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
748                                 luv->uv[0] = ((luv->uv[0]-0.5)*scale)+0.5;
749                         }
750                 }
751         }
752         else {
753                 scale= aspx/aspy;
754
755                 BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
756                         tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
757                         if (!BM_TestHFlag(efa, BM_SELECT))
758                                 continue;
759                         
760                         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
761                                 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
762                                 luv->uv[1] = ((luv->uv[1]-0.5)*scale)+0.5;
763                         }
764                 }
765         }
766 }
767
768 /******************** Map Clip & Correct ******************/
769
770 static void uv_map_clip_correct_properties(wmOperatorType *ot)
771 {
772         RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UV's taking image aspect ratio into account.");
773         RNA_def_boolean(ot->srna, "clip_to_bounds", 0, "Clip to Bounds", "Clip UV coordinates to bounds after unwrapping.");
774         RNA_def_boolean(ot->srna, "scale_to_bounds", 0, "Scale to Bounds", "Scale UV coordinates to bounds after unwrapping.");
775 }
776
777 static void uv_map_clip_correct(BMEditMesh *em, wmOperator *op)
778 {
779         BMFace *efa;
780         BMLoop *l;
781         BMIter iter, liter;
782         MLoopUV *luv;
783         float dx, dy, min[2], max[2];
784         int b, nverts;
785         int correct_aspect= RNA_boolean_get(op->ptr, "correct_aspect");
786         int clip_to_bounds= RNA_boolean_get(op->ptr, "clip_to_bounds");
787         int scale_to_bounds= RNA_boolean_get(op->ptr, "scale_to_bounds");
788
789         /* correct for image aspect ratio */
790         if(correct_aspect)
791                 correct_uv_aspect(em);
792
793         if(scale_to_bounds) {
794                 INIT_MINMAX2(min, max);
795                 
796                 BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
797                         if (!BM_TestHFlag(efa, BM_SELECT))
798                                 continue;
799
800                         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
801                                 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
802                                 DO_MINMAX2(luv->uv, min, max);
803                         }
804                 }
805                 
806                 /* rescale UV to be in 1/1 */
807                 dx= (max[0]-min[0]);
808                 dy= (max[1]-min[1]);
809
810                 if(dx > 0.0f)
811                         dx= 1.0f/dx;
812                 if(dy > 0.0f)
813                         dy= 1.0f/dy;
814
815                 BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
816                         if (!BM_TestHFlag(efa, BM_SELECT))
817                                 continue;
818
819                         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
820                                 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
821                                 
822                                 luv->uv[0] = (luv->uv[0]-min[0])*dx;
823                                 luv->uv[1] = (luv->uv[1]-min[1])*dy;
824                         }
825                 }
826         }
827         else if(clip_to_bounds) {
828                 /* clipping and wrapping */
829                 BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
830                         if (!BM_TestHFlag(efa, BM_SELECT))
831                                 continue;
832
833                         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
834                                 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
835                                 CLAMP(luv->uv[0], 0.0, 1.0);
836                                 CLAMP(luv->uv[1], 0.0, 1.0);
837                         }
838                 }
839         }
840 }
841
842 /* ******************** Unwrap operator **************** */
843
844 static int unwrap_exec(bContext *C, wmOperator *op)
845 {
846         Scene *scene= CTX_data_scene(C);
847         Object *obedit= CTX_data_edit_object(C);
848         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
849         ParamHandle *handle;
850         int method = RNA_enum_get(op->ptr, "method");
851         int fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
852         int correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
853         
854         /* add uvs if they don't exist yet */
855         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
856                 return OPERATOR_CANCELLED;
857         }
858
859         handle= construct_param_handle(scene, em, 0, fill_holes, 0, correct_aspect);
860
861         param_lscm_begin(handle, PARAM_FALSE, method == 0);
862         param_lscm_solve(handle);
863         param_lscm_end(handle);
864         
865         param_pack(handle, scene->toolsettings->uvcalc_margin);
866
867         param_flush(handle);
868
869         param_delete(handle);
870
871         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
872         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
873
874         return OPERATOR_FINISHED;
875 }
876
877 void UV_OT_unwrap(wmOperatorType *ot)
878 {
879         static EnumPropertyItem method_items[] = {
880                 {0, "ANGLE_BASED", 0, "Angle Based", ""},
881                 {1, "CONFORMAL", 0, "Conformal", ""},
882                 {0, NULL, 0, NULL, NULL}};
883
884         /* identifiers */
885         ot->name= "Unwrap";
886         ot->idname= "UV_OT_unwrap";
887         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
888         
889         /* api callbacks */
890         ot->exec= unwrap_exec;
891         ot->poll= ED_operator_uvmap;
892
893         /* properties */
894         RNA_def_enum(ot->srna, "method", method_items, 0, "Method", "Unwrapping method. Angle Based usually gives better results than Conformal, while being somewhat slower.");
895         RNA_def_boolean(ot->srna, "fill_holes", 1, "Fill Holes", "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry.");
896         RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UV's taking image aspect ratio into account.");
897 }
898
899 /**************** Project From View operator **************/
900
901 static void uv_from_view_bounds(float target[2], float source[3], float rotmat[4][4])
902 {
903         float pv[3];
904
905         Mat4MulVecfl(rotmat, pv);
906
907         /* ortho projection */
908         target[0] = -pv[0];
909         target[1] = pv[2];
910 }
911
912 static void uv_from_view(ARegion *ar, float target[2], float source[3], float rotmat[4][4])
913 {
914         RegionView3D *rv3d= ar->regiondata;
915         float pv[3], pv4[4], dx, dy, x= 0.0, y= 0.0;
916
917         Mat4MulVecfl(rotmat, pv);
918
919         dx= ar->winx;
920         dy= ar->winy;
921
922         VecCopyf(pv4, source);
923         pv4[3]= 1.0;
924
925         /* rotmat is the object matrix in this case */
926         Mat4MulVec4fl(rotmat, pv4); 
927
928         /* almost project_short */
929         Mat4MulVec4fl(rv3d->persmat, pv4);
930         if(fabs(pv4[3]) > 0.00001) { /* avoid division by zero */
931                 target[0] = dx/2.0 + (dx/2.0)*pv4[0]/pv4[3];
932                 target[1] = dy/2.0 + (dy/2.0)*pv4[1]/pv4[3];
933         }
934         else {
935                 /* scaling is lost but give a valid result */
936                 target[0] = dx/2.0 + (dx/2.0)*pv4[0];
937                 target[1] = dy/2.0 + (dy/2.0)*pv4[1];
938         }
939
940         /* v3d->persmat seems to do this funky scaling */ 
941         if(dx > dy) {
942                 y= (dx-dy)/2.0;
943                 dy = dx;
944         }
945         else {
946                 x= (dy-dx)/2.0;
947                 dx = dy;
948         }
949
950         target[0]= (x + target[0])/dx;
951         target[1]= (y + target[1])/dy;
952 }
953
954 static int from_view_exec(bContext *C, wmOperator *op)
955 {
956         Scene *scene= CTX_data_scene(C);
957         Object *obedit= CTX_data_edit_object(C);
958         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
959         ARegion *ar= CTX_wm_region(C);
960         BMFace *efa;
961         BMLoop *l;
962         BMIter iter, liter;
963         MLoopUV *luv;
964         float rotmat[4][4];
965
966         /* add uvs if they don't exist yet */
967         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
968                 return OPERATOR_CANCELLED;
969         }
970
971         if(RNA_boolean_get(op->ptr, "orthographic")) {
972                 uv_map_rotation_matrix(rotmat, ar->regiondata, obedit, 90.0f, 0.0f, 1.0f);
973                 
974                 BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
975                         if (!BM_TestHFlag(efa, BM_SELECT))
976                                 continue;
977
978                         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
979                                 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
980                                 uv_from_view_bounds(luv->uv, l->v->co, rotmat);
981                         }
982                 }
983         }
984         else {
985                 Mat4CpyMat4(rotmat, obedit->obmat);
986
987                 BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
988                         if (!BM_TestHFlag(efa, BM_SELECT))
989                                 continue;
990
991                         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
992                                 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
993                                 uv_from_view(ar, luv->uv, l->v->co, rotmat);
994                         }
995                 }
996         }
997
998         uv_map_clip_correct(em, op);
999
1000         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1001         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1002
1003         return OPERATOR_FINISHED;
1004 }
1005
1006 static int from_view_poll(bContext *C)
1007 {
1008         RegionView3D *rv3d= CTX_wm_region_view3d(C);
1009
1010         if(!ED_operator_uvmap(C))
1011                 return 0;
1012
1013         return (rv3d != NULL);
1014 }
1015
1016 void UV_OT_from_view(wmOperatorType *ot)
1017 {
1018         /* identifiers */
1019         ot->name= "Project From View";
1020         ot->idname= "UV_OT_project_from_view";
1021         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1022         
1023         /* api callbacks */
1024         ot->exec= from_view_exec;
1025         ot->poll= from_view_poll;
1026
1027         /* properties */
1028         RNA_def_boolean(ot->srna, "orthographic", 0, "Orthographic", "Use orthographic projection.");
1029         uv_map_clip_correct_properties(ot);
1030 }
1031
1032 /********************** Reset operator ********************/
1033
1034 static int reset_exec(bContext *C, wmOperator *op)
1035 {
1036         Scene *scene= CTX_data_scene(C);
1037         Object *obedit= CTX_data_edit_object(C);
1038         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
1039         BMFace *efa;
1040         BMLoop *l;
1041         BMIter iter, liter;
1042         MTexPoly *tf;
1043         MLoopUV *luv;
1044         BLI_array_declare(uvs);
1045         float **uvs = NULL;
1046         int i;
1047
1048         /* add uvs if they don't exist yet */
1049         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1050                 return OPERATOR_CANCELLED;
1051         }
1052
1053         BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
1054                 if (!BM_TestHFlag(efa, BM_SELECT))
1055                         continue;
1056                 
1057                 BLI_array_empty(uvs);
1058                 i = 0;
1059                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
1060                         luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1061                         BLI_array_growone(uvs);
1062
1063                         uvs[i++] = luv->uv;
1064                 }
1065
1066                 if (i == 3) {
1067                         uvs[0][0] = 0.0;
1068                         uvs[0][1] = 0.0;
1069                         
1070                         uvs[1][0] = 1.0;
1071                         uvs[1][1] = 0.0;
1072
1073                         uvs[2][0] = 1.0;
1074                         uvs[2][1] = 1.0;
1075                 } else if (i == 4) {
1076                         uvs[0][0] = 0.0;
1077                         uvs[0][1] = 0.0;
1078                         
1079                         uvs[1][0] = 1.0;
1080                         uvs[1][1] = 0.0;
1081
1082                         uvs[2][0] = 1.0;
1083                         uvs[2][1] = 1.0;
1084
1085                         uvs[3][0] = 0.0;
1086                         uvs[3][1] = 1.0;
1087                   /*make sure we ignore 2-sided faces*/
1088                 } else if (i > 2) {
1089                         float fac = 0.0f, dfac = 1.0f / (float)efa->len;
1090
1091                         dfac *= M_PI*2;
1092
1093                         for (i=0; i<efa->len; i++) {
1094                                 uvs[i][0] = sin(fac);
1095                                 uvs[i][1] = cos(fac);
1096
1097                                 fac += dfac;
1098                         }
1099                 }
1100         }
1101
1102         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1103         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1104         
1105         BLI_array_free(uvs);
1106
1107         return OPERATOR_FINISHED;
1108 }
1109
1110 void UV_OT_reset(wmOperatorType *ot)
1111 {
1112         /* identifiers */
1113         ot->name= "Reset";
1114         ot->idname= "UV_OT_reset";
1115         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1116         
1117         /* api callbacks */
1118         ot->exec= reset_exec;
1119         ot->poll= ED_operator_uvmap;
1120 }
1121
1122 /****************** Sphere Project operator ***************/
1123
1124 static void uv_sphere_project(float target[2], float source[3], float center[3], float rotmat[4][4])
1125 {
1126         float pv[3];
1127
1128         VecSubf(pv, source, center);
1129         Mat4MulVecfl(rotmat, pv);
1130
1131         spheremap(pv[0], pv[1], pv[2], &target[0], &target[1]);
1132
1133         /* split line is always zero */
1134         if(target[0] >= 1.0f)
1135                 target[0] -= 1.0f;  
1136 }
1137
1138 static void uv_map_mirror(BMEditMesh *em, BMFace *efa, MTexPoly *tf)
1139 {
1140         BMLoop *l;
1141         BMIter liter;
1142         MLoopUV *luv;
1143         BLI_array_declare(uvs);
1144         float **uvs = NULL;
1145         float dx;
1146         int i, mi;
1147
1148         i = 0;
1149         BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
1150                 luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1151                 BLI_array_growone(uvs);
1152
1153                 uvs[i] = luv->uv;
1154                 i++;
1155         }
1156
1157         mi = 0;
1158         for(i=1; i<efa->len; i++)
1159                 if(uvs[i][0] > uvs[mi][0])
1160                         mi = i;
1161
1162         for(i=0; i<efa->len; i++) {
1163                 if(i != mi) {
1164                         dx = uvs[mi][0] - uvs[i][0];
1165                         if(dx > 0.5) uvs[i][0] += 1.0;
1166                 } 
1167         } 
1168
1169         BLI_array_free(uvs);
1170 }
1171
1172 static int sphere_project_exec(bContext *C, wmOperator *op)
1173 {
1174         Scene *scene= CTX_data_scene(C);
1175         Object *obedit= CTX_data_edit_object(C);
1176         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
1177         BMFace *efa;
1178         BMLoop *l;
1179         BMIter iter, liter;
1180         MTexPoly *tf;
1181         MLoopUV *luv;
1182         float center[3], rotmat[4][4];
1183
1184         /* add uvs if they don't exist yet */
1185         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1186                 return OPERATOR_CANCELLED;
1187         }
1188
1189         uv_map_transform(C, op, center, rotmat);
1190
1191         BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
1192                 if (!BM_TestHFlag(efa, BM_SELECT))
1193                         continue;
1194
1195                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
1196                         luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1197
1198                         uv_sphere_project(luv->uv, l->v->co, center, rotmat);
1199                 }
1200
1201                 tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
1202                 uv_map_mirror(em, efa, tf);
1203         }
1204
1205         uv_map_clip_correct(em, op);
1206
1207         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1208         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1209
1210         return OPERATOR_FINISHED;
1211 }
1212
1213 void UV_OT_sphere_project(wmOperatorType *ot)
1214 {
1215         /* identifiers */
1216         ot->name= "Sphere Projection";
1217         ot->idname= "UV_OT_sphere_project";
1218         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1219         
1220         /* api callbacks */
1221         ot->exec= sphere_project_exec;
1222         ot->poll= ED_operator_uvmap;
1223
1224         /* properties */
1225         uv_transform_properties(ot, 0);
1226         uv_map_clip_correct_properties(ot);
1227 }
1228
1229 /***************** Cylinder Project operator **************/
1230
1231 static void uv_cylinder_project(float target[2], float source[3], float center[3], float rotmat[4][4])
1232 {
1233         float pv[3];
1234
1235         VecSubf(pv, source, center);
1236         Mat4MulVecfl(rotmat, pv);
1237
1238         tubemap(pv[0], pv[1], pv[2], &target[0], &target[1]);
1239
1240         /* split line is always zero */
1241         if(target[0] >= 1.0f)
1242                 target[0] -= 1.0f;  
1243 }
1244
1245 static int cylinder_project_exec(bContext *C, wmOperator *op)
1246 {
1247         Scene *scene= CTX_data_scene(C);
1248         Object *obedit= CTX_data_edit_object(C);
1249         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
1250         BMFace *efa;
1251         BMLoop *l;
1252         BMIter iter, liter;
1253         MTexPoly *tf;
1254         MLoopUV *luv;
1255         float center[3], rotmat[4][4];
1256
1257         /* add uvs if they don't exist yet */
1258         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1259                 return OPERATOR_CANCELLED;
1260         }
1261
1262         uv_map_transform(C, op, center, rotmat);
1263
1264         BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
1265                 tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
1266                 if (!BM_TestHFlag(efa, BM_SELECT))
1267                         continue;
1268                 
1269                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
1270                         luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1271
1272                         uv_cylinder_project(luv->uv, l->v->co, center, rotmat);
1273                 }
1274
1275                 uv_map_mirror(em, efa, tf);
1276         }
1277
1278         uv_map_clip_correct(em, op);
1279
1280         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1281         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1282
1283         return OPERATOR_FINISHED;
1284 }
1285
1286 void UV_OT_cylinder_project(wmOperatorType *ot)
1287 {
1288         /* identifiers */
1289         ot->name= "Cylinder Projection";
1290         ot->idname= "UV_OT_cylinder_project";
1291         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1292         
1293         /* api callbacks */
1294         ot->exec= cylinder_project_exec;
1295         ot->poll= ED_operator_uvmap;
1296
1297         /* properties */
1298         uv_transform_properties(ot, 1);
1299         uv_map_clip_correct_properties(ot);
1300 }
1301
1302 /******************* Cube Project operator ****************/
1303
1304 static int cube_project_exec(bContext *C, wmOperator *op)
1305 {
1306         Scene *scene= CTX_data_scene(C);
1307         Object *obedit= CTX_data_edit_object(C);
1308         BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
1309         BMFace *efa;
1310         BMLoop *l;
1311         BMIter iter, liter;
1312         MTexPoly *tf;
1313         MLoopUV *luv;
1314         float no[3], cube_size, *loc, dx, dy;
1315         int cox, coy;
1316
1317         /* add uvs if they don't exist yet */
1318         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1319                 return OPERATOR_CANCELLED;
1320         }
1321
1322         loc= obedit->obmat[3];
1323         cube_size= RNA_float_get(op->ptr, "cube_size");
1324
1325         /* choose x,y,z axis for projection depending on the largest normal
1326          * component, but clusters all together around the center of map. */
1327
1328         BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
1329                 int first=1;
1330
1331                 tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
1332                 if (!BM_TestHFlag(efa, BM_SELECT))
1333                         continue;
1334                 
1335                 VECCOPY(no, efa->no);
1336
1337                 no[0]= fabs(no[0]);
1338                 no[1]= fabs(no[1]);
1339                 no[2]= fabs(no[2]);
1340                 
1341                 cox=0; coy= 1;
1342                 if(no[2]>=no[0] && no[2]>=no[1]);
1343                 else if(no[1]>=no[0] && no[1]>=no[2]) coy= 2;
1344                 else { cox= 1; coy= 2; }
1345
1346                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) {
1347                         luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1348
1349                         luv->uv[0] = 0.5+0.5*cube_size*(loc[cox] + l->v->co[cox]);
1350                         luv->uv[1] = 0.5+0.5*cube_size*(loc[coy] + l->v->co[coy]);
1351                         
1352                         if (first) {
1353                                 dx = floor(luv->uv[0]);
1354                                 dy = floor(luv->uv[1]);
1355                                 first = 0;
1356                         }
1357
1358                         luv->uv[0] -= dx;
1359                         luv->uv[1] -= dy;
1360                 }
1361         }
1362
1363         uv_map_clip_correct(em, op);
1364
1365         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1366         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1367
1368         return OPERATOR_FINISHED;
1369 }
1370
1371 void UV_OT_cube_project(wmOperatorType *ot)
1372 {
1373         /* identifiers */
1374         ot->name= "Cube Projection";
1375         ot->idname= "UV_OT_cube_project";
1376         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1377         
1378         /* api callbacks */
1379         ot->exec= cube_project_exec;
1380         ot->poll= ED_operator_uvmap;
1381
1382         /* properties */
1383         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);
1384         uv_map_clip_correct_properties(ot);
1385 }
1386
1387 /******************* Mapping Menu operator ****************/
1388
1389 static int mapping_menu_invoke(bContext *C, wmOperator *op, wmEvent *event)
1390 {
1391         uiPopupMenu *pup;
1392         uiLayout *layout;
1393
1394         pup= uiPupMenuBegin(C, "UV Mapping", 0);
1395         layout= uiPupMenuLayout(pup);
1396
1397         uiItemO(layout, NULL, 0, "UV_OT_unwrap");
1398         uiItemS(layout);
1399         uiItemO(layout, NULL, 0, "UV_OT_cube_project");
1400         uiItemO(layout, NULL, 0, "UV_OT_cylinder_project");
1401         uiItemO(layout, NULL, 0, "UV_OT_sphere_project");
1402         uiItemO(layout, NULL, 0, "UV_OT_project_from_view");
1403         uiItemS(layout);
1404         uiItemO(layout, NULL, 0, "UV_OT_reset");
1405
1406         uiPupMenuEnd(C, pup);
1407
1408         /* XXX python */
1409 #ifndef DISABLE_PYTHON
1410 #if 0
1411         /* note that we account for the 10 previous entries with i+10: */
1412         for(pym = BPyMenuTable[PYMENU_UVCALCULATION]; pym; pym = pym->next, i++) {
1413                 
1414                 if(!has_pymenu) {
1415                         strcat(uvmenu, "|%l");
1416                         has_pymenu = 1;
1417                 }
1418                 
1419                 strcat(uvmenu, "|");
1420                 strcat(uvmenu, pym->name);
1421                 strcat(uvmenu, " %x");
1422                 sprintf(menu_number, "%d", i+10);
1423                 strcat(uvmenu, menu_number);
1424         }
1425 #endif
1426 #endif
1427         
1428 #ifndef DISABLE_PYTHON
1429 #if 0
1430         mode= pupmenu(uvmenu);
1431         if(mode >= 10) {
1432                 BPY_menu_do_python(PYMENU_UVCALCULATION, mode - 10);
1433                 return;
1434         }
1435 #endif
1436 #endif  
1437
1438         return OPERATOR_CANCELLED;
1439 }
1440
1441 void UV_OT_mapping_menu(wmOperatorType *ot)
1442 {
1443         /* identifiers */
1444         ot->name= "UV Mapping";
1445         ot->idname= "UV_OT_mapping_menu";
1446         
1447         /* api callbacks */
1448         ot->invoke= mapping_menu_invoke;
1449         ot->poll= ED_operator_uvmap;
1450 }
1451