Fix for [#22741] Material preview doesn't update when "undo" is used to revert a...
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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_camera_types.h"
37 #include "DNA_meshdata_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
40
41 #include "BKE_context.h"
42 #include "BKE_customdata.h"
43 #include "BKE_depsgraph.h"
44 #include "BKE_image.h"
45 #include "BKE_mesh.h"
46
47 #include "BLI_math.h"
48 #include "BLI_edgehash.h"
49 #include "BLI_editVert.h"
50 #include "BLI_uvproject.h"
51
52 #include "PIL_time.h"
53
54 #include "ED_image.h"
55 #include "ED_mesh.h"
56 #include "ED_screen.h"
57 #include "ED_uvedit.h"
58 #include "ED_view3d.h"
59
60 #include "RNA_access.h"
61 #include "RNA_define.h"
62
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "uvedit_intern.h"
68 #include "uvedit_parametrizer.h"
69
70 static int ED_uvedit_ensure_uvs(bContext *C, Scene *scene, Object *obedit)
71 {
72         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
73         EditFace *efa;
74         MTFace *tf;
75         Image *ima;
76         bScreen *sc;
77         ScrArea *sa;
78         SpaceLink *slink;
79         SpaceImage *sima;
80
81         if(ED_uvedit_test(obedit)) {
82                 BKE_mesh_end_editmesh(obedit->data, em);
83                 return 1;
84         }
85
86         if(em && em->faces.first)
87                 EM_add_data_layer(em, &em->fdata, CD_MTFACE);
88         
89         if(!ED_uvedit_test(obedit)) {
90                 BKE_mesh_end_editmesh(obedit->data, em);
91                 return 0;
92         }
93
94         ima= CTX_data_edit_image(C);
95
96         if(!ima) {
97                 /* no image in context in the 3d view, we find first image window .. */
98                 sc= CTX_wm_screen(C);
99
100                 for(sa=sc->areabase.first; sa; sa=sa->next) {
101                         slink= sa->spacedata.first;
102                         if(slink->spacetype == SPACE_IMAGE) {
103                                 sima= (SpaceImage*)slink;
104
105                                 ima= sima->image;
106                                 if(ima) {
107                                         if(ima->type==IMA_TYPE_R_RESULT || ima->type==IMA_TYPE_COMPOSITE)
108                                                 ima= NULL;
109                                         else
110                                                 break;
111                                 }
112                         }
113                 }
114         }
115         
116         if(ima)
117                 ED_uvedit_assign_image(scene, obedit, ima, NULL);
118         
119         /* select new UV's */
120         for(efa=em->faces.first; efa; efa=efa->next) {
121                 tf= (MTFace *)CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
122                 uvedit_face_select(scene, efa, tf);
123         }
124
125         BKE_mesh_end_editmesh(obedit->data, em);
126         return 1;
127 }
128
129 /****************** Parametrizer Conversion ***************/
130
131 ParamHandle *construct_param_handle(Scene *scene, EditMesh *em, short implicit, short fill, short sel, short correct_aspect)
132 {
133         ParamHandle *handle;
134         EditFace *efa;
135         EditEdge *eed;
136         EditVert *ev;
137         MTFace *tf;
138         int a;
139         
140         handle = param_construct_begin();
141
142         if(correct_aspect) {
143                 efa = EM_get_actFace(em, 1);
144
145                 if(efa) {
146                         MTFace *tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
147                         float aspx, aspy;
148
149                         ED_image_uv_aspect(tf->tpage, &aspx, &aspy);
150                 
151                         if(aspx!=aspy)
152                                 param_aspect_ratio(handle, aspx, aspy);
153                 }
154         }
155         
156         /* we need the vert indicies */
157         for(ev= em->verts.first, a=0; ev; ev= ev->next, a++)
158                 ev->tmp.l = a;
159         
160         for(efa= em->faces.first; efa; efa= efa->next) {
161                 ParamKey key, vkeys[4];
162                 ParamBool pin[4], select[4];
163                 float *co[4];
164                 float *uv[4];
165                 int nverts;
166                 
167                 if((efa->h) || (sel && (efa->f & SELECT)==0)) 
168                         continue;
169
170                 tf= (MTFace *)CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
171                 
172                 if(implicit &&
173                         !(      uvedit_uv_selected(scene, efa, tf, 0) ||
174                                 uvedit_uv_selected(scene, efa, tf, 1) ||
175                                 uvedit_uv_selected(scene, efa, tf, 2) ||
176                                 (efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) )
177                 ) {
178                         continue;
179                 }
180
181                 key = (ParamKey)efa;
182                 vkeys[0] = (ParamKey)efa->v1->tmp.l;
183                 vkeys[1] = (ParamKey)efa->v2->tmp.l;
184                 vkeys[2] = (ParamKey)efa->v3->tmp.l;
185
186                 co[0] = efa->v1->co;
187                 co[1] = efa->v2->co;
188                 co[2] = efa->v3->co;
189
190                 uv[0] = tf->uv[0];
191                 uv[1] = tf->uv[1];
192                 uv[2] = tf->uv[2];
193
194                 pin[0] = ((tf->unwrap & TF_PIN1) != 0);
195                 pin[1] = ((tf->unwrap & TF_PIN2) != 0);
196                 pin[2] = ((tf->unwrap & TF_PIN3) != 0);
197
198                 select[0] = ((uvedit_uv_selected(scene, efa, tf, 0)) != 0);
199                 select[1] = ((uvedit_uv_selected(scene, efa, tf, 1)) != 0);
200                 select[2] = ((uvedit_uv_selected(scene, efa, tf, 2)) != 0);
201
202                 if(efa->v4) {
203                         vkeys[3] = (ParamKey)efa->v4->tmp.l;
204                         co[3] = efa->v4->co;
205                         uv[3] = tf->uv[3];
206                         pin[3] = ((tf->unwrap & TF_PIN4) != 0);
207                         select[3] = (uvedit_uv_selected(scene, efa, tf, 3) != 0);
208                         nverts = 4;
209                 }
210                 else
211                         nverts = 3;
212
213                 param_face_add(handle, key, nverts, vkeys, co, uv, pin, select);
214         }
215
216         if(!implicit) {
217                 for(eed= em->edges.first; eed; eed= eed->next) {
218                         if(eed->seam) {
219                                 ParamKey vkeys[2];
220                                 vkeys[0] = (ParamKey)eed->v1->tmp.l;
221                                 vkeys[1] = (ParamKey)eed->v2->tmp.l;
222                                 param_edge_set_seam(handle, vkeys);
223                         }
224                 }
225         }
226
227         param_construct_end(handle, fill, implicit);
228
229         return handle;
230 }
231
232 /* ******************** Minimize Stretch operator **************** */
233
234 typedef struct MinStretch {
235         Scene *scene;
236         Object *obedit;
237         EditMesh *em;
238         ParamHandle *handle;
239         float blend;
240         double lasttime;
241         int i, iterations;
242         wmTimer *timer;
243 } MinStretch;
244
245 static void minimize_stretch_init(bContext *C, wmOperator *op)
246 {
247         Scene *scene= CTX_data_scene(C);
248         Object *obedit= CTX_data_edit_object(C);
249         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
250         MinStretch *ms;
251         int fill_holes= RNA_boolean_get(op->ptr, "fill_holes");
252
253         ms= MEM_callocN(sizeof(MinStretch), "MinStretch");
254         ms->scene= scene;
255         ms->obedit= obedit;
256         ms->em= em;
257         ms->blend= RNA_float_get(op->ptr, "blend");
258         ms->iterations= RNA_int_get(op->ptr, "iterations");
259         ms->handle= construct_param_handle(scene, em, 1, fill_holes, 1, 1);
260         ms->lasttime= PIL_check_seconds_timer();
261
262         param_stretch_begin(ms->handle);
263         if(ms->blend != 0.0f)
264                 param_stretch_blend(ms->handle, ms->blend);
265
266         op->customdata= ms;
267 }
268
269 static void minimize_stretch_iteration(bContext *C, wmOperator *op, int interactive)
270 {
271         MinStretch *ms= op->customdata;
272         ScrArea *sa= CTX_wm_area(C);
273
274         param_stretch_blend(ms->handle, ms->blend);
275         param_stretch_iter(ms->handle);
276
277         if(interactive && (PIL_check_seconds_timer() - ms->lasttime > 0.5)) {
278                 char str[100];
279
280                 param_flush(ms->handle);
281
282                 if(sa) {
283                         sprintf(str, "Minimize Stretch. Blend %.2f.", ms->blend);
284                         ED_area_headerprint(sa, str);
285                 }
286
287                 ms->lasttime = PIL_check_seconds_timer();
288
289                 DAG_id_flush_update(ms->obedit->data, OB_RECALC_DATA);
290                 WM_event_add_notifier(C, NC_GEOM|ND_DATA, ms->obedit->data);
291         }
292 }
293
294 static void minimize_stretch_exit(bContext *C, wmOperator *op, int cancel)
295 {
296         MinStretch *ms= op->customdata;
297         ScrArea *sa= CTX_wm_area(C);
298
299         if(sa)
300                 ED_area_headerprint(sa, NULL);
301         if(ms->timer)
302                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), ms->timer);
303
304         if(cancel)
305                 param_flush_restore(ms->handle);
306         else
307                 param_flush(ms->handle);
308
309         param_stretch_end(ms->handle);
310         param_delete(ms->handle);
311
312         DAG_id_flush_update(ms->obedit->data, OB_RECALC_DATA);
313         WM_event_add_notifier(C, NC_GEOM|ND_DATA, ms->obedit->data);
314
315         MEM_freeN(ms);
316         op->customdata= NULL;
317 }
318
319 static int minimize_stretch_exec(bContext *C, wmOperator *op)
320 {
321         int i, iterations;
322
323         minimize_stretch_init(C, op);
324
325         iterations= RNA_int_get(op->ptr, "iterations");
326         for(i=0; i<iterations; i++)
327                 minimize_stretch_iteration(C, op, 0);
328         minimize_stretch_exit(C, op, 0);
329
330         return OPERATOR_FINISHED;
331 }
332
333 static int minimize_stretch_invoke(bContext *C, wmOperator *op, wmEvent *event)
334 {
335         MinStretch *ms;
336
337         minimize_stretch_init(C, op);
338         minimize_stretch_iteration(C, op, 1);
339
340         ms= op->customdata;
341         WM_event_add_modal_handler(C, op);
342         ms->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
343
344         return OPERATOR_RUNNING_MODAL;
345 }
346
347 static int minimize_stretch_modal(bContext *C, wmOperator *op, wmEvent *event)
348 {
349         MinStretch *ms= op->customdata;
350
351         switch(event->type) {
352                 case ESCKEY:
353                 case RIGHTMOUSE:
354                         minimize_stretch_exit(C, op, 1);
355                         return OPERATOR_CANCELLED;
356                 case RETKEY:
357                 case PADENTER:
358                 case LEFTMOUSE:
359                         minimize_stretch_exit(C, op, 0);
360                         return OPERATOR_FINISHED;
361                 case PADPLUSKEY:
362                 case WHEELUPMOUSE:
363                         if(ms->blend < 0.95f) {
364                                 ms->blend += 0.1f;
365                                 ms->lasttime= 0.0f;
366                                 RNA_float_set(op->ptr, "blend", ms->blend);
367                                 minimize_stretch_iteration(C, op, 1);
368                         }
369                         break;
370                 case PADMINUS:
371                 case WHEELDOWNMOUSE:
372                         if(ms->blend > 0.05f) {
373                                 ms->blend -= 0.1f;
374                                 ms->lasttime= 0.0f;
375                                 RNA_float_set(op->ptr, "blend", ms->blend);
376                                 minimize_stretch_iteration(C, op, 1);
377                         }
378                         break;
379                 case TIMER:
380                         if(ms->timer == event->customdata) {
381                                 double start= PIL_check_seconds_timer();
382
383                                 do {
384                                         minimize_stretch_iteration(C, op, 1);
385                                 } while(PIL_check_seconds_timer() - start < 0.01);
386                         }
387                         break;
388         }
389
390         if(ms->iterations && ms->i >= ms->iterations) {
391                 minimize_stretch_exit(C, op, 0);
392                 return OPERATOR_FINISHED;
393         }
394
395         return OPERATOR_RUNNING_MODAL;
396 }
397
398 static int minimize_stretch_cancel(bContext *C, wmOperator *op)
399 {
400         minimize_stretch_exit(C, op, 1);
401
402         return OPERATOR_CANCELLED;
403 }
404
405 void UV_OT_minimize_stretch(wmOperatorType *ot)
406 {
407         /* identifiers */
408         ot->name= "Minimize Stretch";
409         ot->idname= "UV_OT_minimize_stretch";
410         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
411         ot->description="Reduce UV stretching by relaxing angles";
412         
413         /* api callbacks */
414         ot->exec= minimize_stretch_exec;
415         ot->invoke= minimize_stretch_invoke;
416         ot->modal= minimize_stretch_modal;
417         ot->cancel= minimize_stretch_cancel;
418         ot->poll= ED_operator_uvedit;
419
420         /* properties */
421         RNA_def_boolean(ot->srna, "fill_holes", 1, "Fill Holes", "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry.");
422         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);
423         RNA_def_int(ot->srna, "iterations", 0, 0, INT_MAX, "Iterations", "Number of iterations to run, 0 is unlimited when run interactively.", 0, 100);
424 }
425
426 /* ******************** Pack Islands operator **************** */
427
428 static int pack_islands_exec(bContext *C, wmOperator *op)
429 {
430         Scene *scene= CTX_data_scene(C);
431         Object *obedit= CTX_data_edit_object(C);
432         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
433         ParamHandle *handle;
434
435         handle = construct_param_handle(scene, em, 1, 0, 1, 1);
436         param_pack(handle, scene->toolsettings->uvcalc_margin);
437         param_flush(handle);
438         param_delete(handle);
439         
440         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
441         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
442
443         BKE_mesh_end_editmesh(obedit->data, em);
444         return OPERATOR_FINISHED;
445 }
446
447 void UV_OT_pack_islands(wmOperatorType *ot)
448 {
449         /* identifiers */
450         ot->name= "Pack Islands";
451         ot->idname= "UV_OT_pack_islands";
452         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
453         
454         /* api callbacks */
455         ot->exec= pack_islands_exec;
456         ot->poll= ED_operator_uvedit;
457 }
458
459 /* ******************** Average Islands Scale operator **************** */
460
461 static int average_islands_scale_exec(bContext *C, wmOperator *op)
462 {
463         Scene *scene= CTX_data_scene(C);
464         Object *obedit= CTX_data_edit_object(C);
465         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
466         ParamHandle *handle;
467
468         handle= construct_param_handle(scene, em, 1, 0, 1, 1);
469         param_average(handle);
470         param_flush(handle);
471         param_delete(handle);
472         
473         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
474         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
475
476         BKE_mesh_end_editmesh(obedit->data, em);
477         return OPERATOR_FINISHED;
478 }
479
480 void UV_OT_average_islands_scale(wmOperatorType *ot)
481 {
482         /* identifiers */
483         ot->name= "Average Islands Scale";
484         ot->idname= "UV_OT_average_islands_scale";
485         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
486         
487         /* api callbacks */
488         ot->exec= average_islands_scale_exec;
489         ot->poll= ED_operator_uvedit;
490 }
491
492 /**************** Live Unwrap *****************/
493
494 static ParamHandle *liveHandle = NULL;
495
496 void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
497 {
498         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
499         short abf = scene->toolsettings->unwrapper == 1;
500         short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES;
501
502         if(!ED_uvedit_test(obedit)) {
503                 BKE_mesh_end_editmesh(obedit->data, em);
504                 return;
505         }
506
507         liveHandle = construct_param_handle(scene, em, 0, fillholes, 1, 1);
508
509         param_lscm_begin(liveHandle, PARAM_TRUE, abf);
510         BKE_mesh_end_editmesh(obedit->data, em);
511 }
512
513 void ED_uvedit_live_unwrap_re_solve(void)
514 {
515         if(liveHandle) {
516                 param_lscm_solve(liveHandle);
517                 param_flush(liveHandle);
518         }
519 }
520         
521 void ED_uvedit_live_unwrap_end(short cancel)
522 {
523         if(liveHandle) {
524                 param_lscm_end(liveHandle);
525                 if(cancel)
526                         param_flush_restore(liveHandle);
527                 param_delete(liveHandle);
528                 liveHandle = NULL;
529         }
530 }
531
532 /*************** UV Map Common Transforms *****************/
533
534 #define VIEW_ON_EQUATOR 0
535 #define VIEW_ON_POLES   1
536 #define ALIGN_TO_OBJECT 2
537
538 #define POLAR_ZX        0
539 #define POLAR_ZY        1
540
541 static void uv_map_transform_center(Scene *scene, View3D *v3d, float *result, Object *ob, EditMesh *em)
542 {
543         EditFace *efa;
544         float min[3], max[3], *cursx;
545         int around= (v3d)? v3d->around: V3D_CENTER;
546
547         /* only operates on the edit object - this is all that's needed now */
548
549         switch(around)  {
550                 case V3D_CENTER: /* bounding box center */
551                         min[0]= min[1]= min[2]= 1e20f;
552                         max[0]= max[1]= max[2]= -1e20f; 
553
554                         for(efa= em->faces.first; efa; efa= efa->next) {
555                                 if(efa->f & SELECT) {
556                                         DO_MINMAX(efa->v1->co, min, max);
557                                         DO_MINMAX(efa->v2->co, min, max);
558                                         DO_MINMAX(efa->v3->co, min, max);
559                                         if(efa->v4) DO_MINMAX(efa->v4->co, min, max);
560                                 }
561                         }
562                         mid_v3_v3v3(result, min, max);
563                         break;
564
565                 case V3D_CURSOR: /*cursor center*/ 
566                         cursx= give_cursor(scene, v3d);
567                         /* shift to objects world */
568                         result[0]= cursx[0]-ob->obmat[3][0];
569                         result[1]= cursx[1]-ob->obmat[3][1];
570                         result[2]= cursx[2]-ob->obmat[3][2];
571                         break;
572
573                 case V3D_LOCAL: /*object center*/
574                 case V3D_CENTROID: /* multiple objects centers, only one object here*/
575                 default:
576                         result[0]= result[1]= result[2]= 0.0;
577                         break;
578         }
579 }
580
581 static void uv_map_rotation_matrix(float result[][4], RegionView3D *rv3d, Object *ob, float upangledeg, float sideangledeg, float radius)
582 {
583         float rotup[4][4], rotside[4][4], viewmatrix[4][4], rotobj[4][4];
584         float sideangle= 0.0f, upangle= 0.0f;
585         int k;
586
587         /* get rotation of the current view matrix */
588         if(rv3d)
589                 copy_m4_m4(viewmatrix, rv3d->viewmat);
590         else
591                 unit_m4(viewmatrix);
592
593         /* but shifting */
594         for(k=0; k<4; k++)
595                 viewmatrix[3][k] =0.0f;
596
597         /* get rotation of the current object matrix */
598         copy_m4_m4(rotobj,ob->obmat);
599
600         /* but shifting */
601         for(k=0; k<4; k++)
602                 rotobj[3][k] =0.0f;
603
604         zero_m4(rotup);
605         zero_m4(rotside);
606
607         /* compensate front/side.. against opengl x,y,z world definition */
608         /* this is "kanonen gegen spatzen", a few plus minus 1 will do here */
609         /* i wanted to keep the reason here, so we're rotating*/
610         sideangle= (float)M_PI*(sideangledeg + 180.0f)/180.0f;
611         rotside[0][0]= (float)cos(sideangle);
612         rotside[0][1]= -(float)sin(sideangle);
613         rotside[1][0]= (float)sin(sideangle);
614         rotside[1][1]= (float)cos(sideangle);
615         rotside[2][2]= 1.0f;
616       
617         upangle= (float)M_PI*upangledeg/180.0f;
618         rotup[1][1]= (float)cos(upangle)/radius;
619         rotup[1][2]= -(float)sin(upangle)/radius;
620         rotup[2][1]= (float)sin(upangle)/radius;
621         rotup[2][2]= (float)cos(upangle)/radius;
622         rotup[0][0]= (float)1.0f/radius;
623
624         /* calculate transforms*/
625         mul_serie_m4(result, rotup, rotside, viewmatrix, rotobj, NULL, NULL, NULL, NULL);
626 }
627
628 static void uv_map_transform(bContext *C, wmOperator *op, float center[3], float rotmat[4][4])
629 {
630         /* context checks are messy here, making it work in both 3d view and uv editor */
631         Scene *scene= CTX_data_scene(C);
632         Object *obedit= CTX_data_edit_object(C);
633         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
634         View3D *v3d= CTX_wm_view3d(C);
635         RegionView3D *rv3d= CTX_wm_region_view3d(C);
636         /* common operator properties */
637         int align= RNA_enum_get(op->ptr, "align");
638         int direction= RNA_enum_get(op->ptr, "direction");
639         float radius= RNA_struct_find_property(op->ptr, "radius")? RNA_float_get(op->ptr, "radius"): 1.0f;
640         float upangledeg, sideangledeg;
641
642         uv_map_transform_center(scene, v3d, center, obedit, em);
643
644         if(direction == VIEW_ON_EQUATOR) {
645                 upangledeg= 90.0f;
646                 sideangledeg= 0.0f;
647         }
648         else {
649                 upangledeg= 0.0f;
650                 if(align == POLAR_ZY) sideangledeg= 0.0f;
651                 else sideangledeg= 90.0f;
652         }
653
654         /* be compatible to the "old" sphere/cylinder mode */
655         if(direction == ALIGN_TO_OBJECT)
656                 unit_m4(rotmat);
657         else 
658                 uv_map_rotation_matrix(rotmat, rv3d, obedit, upangledeg, sideangledeg, radius);
659
660         BKE_mesh_end_editmesh(obedit->data, em);
661 }
662
663 static void uv_transform_properties(wmOperatorType *ot, int radius)
664 {
665         static EnumPropertyItem direction_items[]= {
666                 {VIEW_ON_EQUATOR, "VIEW_ON_EQUATOR", 0, "View on Equator", "3D view is on the equator"},
667                 {VIEW_ON_POLES, "VIEW_ON_POLES", 0, "View on Poles", "3D view is on the poles"},
668                 {ALIGN_TO_OBJECT, "ALIGN_TO_OBJECT", 0, "Align to Object", "Align according to object transform"},
669                 {0, NULL, 0, NULL, NULL}
670         };
671         static EnumPropertyItem align_items[]= {
672                 {POLAR_ZX, "POLAR_ZX", 0, "Polar ZX", "Polar 0 is X"},
673                 {POLAR_ZY, "POLAR_ZY", 0, "Polar ZY", "Polar 0 is Y"},
674                 {0, NULL, 0, NULL, NULL}
675         };
676
677         RNA_def_enum(ot->srna, "direction", direction_items, VIEW_ON_EQUATOR, "Direction", "Direction of the sphere or cylinder.");
678         RNA_def_enum(ot->srna, "align", align_items, VIEW_ON_EQUATOR, "Align", "How to determine rotation around the pole.");
679         if(radius)
680                 RNA_def_float(ot->srna, "radius", 1.0f, 0.0f, FLT_MAX, "Radius", "Radius of the sphere or cylinder.", 0.0001f, 100.0f);
681 }
682
683 static void correct_uv_aspect(EditMesh *em)
684 {
685         EditFace *efa= EM_get_actFace(em, 1);
686         MTFace *tf;
687         float scale, aspx= 1.0f, aspy=1.0f;
688         
689         if(efa) {
690                 tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
691                 ED_image_uv_aspect(tf->tpage, &aspx, &aspy);
692         }
693         
694         if(aspx == aspy)
695                 return;
696                 
697         if(aspx > aspy) {
698                 scale= aspy/aspx;
699
700                 for(efa= em->faces.first; efa; efa= efa->next) {
701                         if(efa->f & SELECT) {
702                                 tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
703
704                                 tf->uv[0][0]= ((tf->uv[0][0]-0.5)*scale)+0.5;
705                                 tf->uv[1][0]= ((tf->uv[1][0]-0.5)*scale)+0.5;
706                                 tf->uv[2][0]= ((tf->uv[2][0]-0.5)*scale)+0.5;
707                                 if(efa->v4)
708                                         tf->uv[3][0]= ((tf->uv[3][0]-0.5)*scale)+0.5;
709                         }
710                 }
711         }
712         else {
713                 scale= aspx/aspy;
714
715                 for(efa= em->faces.first; efa; efa= efa->next) {
716                         if(efa->f & SELECT) {
717                                 tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
718
719                                 tf->uv[0][1]= ((tf->uv[0][1]-0.5)*scale)+0.5;
720                                 tf->uv[1][1]= ((tf->uv[1][1]-0.5)*scale)+0.5;
721                                 tf->uv[2][1]= ((tf->uv[2][1]-0.5)*scale)+0.5;
722                                 if(efa->v4)
723                                         tf->uv[3][1]= ((tf->uv[3][1]-0.5)*scale)+0.5;
724                         }
725                 }
726         }
727 }
728
729 /******************** Map Clip & Correct ******************/
730
731 static void uv_map_clip_correct_properties(wmOperatorType *ot)
732 {
733         RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UV's taking image aspect ratio into account.");
734         RNA_def_boolean(ot->srna, "clip_to_bounds", 0, "Clip to Bounds", "Clip UV coordinates to bounds after unwrapping.");
735         RNA_def_boolean(ot->srna, "scale_to_bounds", 0, "Scale to Bounds", "Scale UV coordinates to bounds after unwrapping.");
736 }
737
738 static void uv_map_clip_correct(EditMesh *em, wmOperator *op)
739 {
740         EditFace *efa;
741         MTFace *tf;
742         float dx, dy, min[2], max[2];
743         int b, nverts;
744         int correct_aspect= RNA_boolean_get(op->ptr, "correct_aspect");
745         int clip_to_bounds= RNA_boolean_get(op->ptr, "clip_to_bounds");
746         int scale_to_bounds= RNA_boolean_get(op->ptr, "scale_to_bounds");
747
748         /* correct for image aspect ratio */
749         if(correct_aspect)
750                 correct_uv_aspect(em);
751
752         if(scale_to_bounds) {
753                 INIT_MINMAX2(min, max);
754                 
755                 for(efa= em->faces.first; efa; efa= efa->next) {
756                         if(efa->f & SELECT) {
757                                 tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
758
759                                 DO_MINMAX2(tf->uv[0], min, max);
760                                 DO_MINMAX2(tf->uv[1], min, max);
761                                 DO_MINMAX2(tf->uv[2], min, max);
762
763                                 if(efa->v4)
764                                         DO_MINMAX2(tf->uv[3], min, max);
765                         }
766                 }
767                 
768                 /* rescale UV to be in 1/1 */
769                 dx= (max[0]-min[0]);
770                 dy= (max[1]-min[1]);
771
772                 if(dx > 0.0f)
773                         dx= 1.0f/dx;
774                 if(dy > 0.0f)
775                         dy= 1.0f/dy;
776
777                 for(efa= em->faces.first; efa; efa= efa->next) {
778                         if(efa->f & SELECT) {
779                                 tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
780
781                                 nverts= (efa->v4)? 4: 3;
782
783                                 for(b=0; b<nverts; b++) {
784                                         tf->uv[b][0]= (tf->uv[b][0]-min[0])*dx;
785                                         tf->uv[b][1]= (tf->uv[b][1]-min[1])*dy;
786                                 }
787                         }
788                 }
789         }
790         else if(clip_to_bounds) {
791                 /* clipping and wrapping */
792                 for(efa= em->faces.first; efa; efa= efa->next) {
793                         if(efa->f & SELECT) {
794                                 tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
795                         
796                                 nverts= (efa->v4)? 4: 3;
797
798                                 for(b=0; b<nverts; b++) {
799                                         CLAMP(tf->uv[b][0], 0.0, 1.0);
800                                         CLAMP(tf->uv[b][1], 0.0, 1.0);
801                                 }
802                         }
803                 }
804         }
805 }
806
807 /* ******************** Unwrap operator **************** */
808
809 static int unwrap_exec(bContext *C, wmOperator *op)
810 {
811         Scene *scene= CTX_data_scene(C);
812         Object *obedit= CTX_data_edit_object(C);
813         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
814         ParamHandle *handle;
815         int method = RNA_enum_get(op->ptr, "method");
816         int fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
817         int correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
818         
819         /* add uvs if they don't exist yet */
820         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
821                 BKE_mesh_end_editmesh(obedit->data, em);
822                 return OPERATOR_CANCELLED;
823         }
824
825         handle= construct_param_handle(scene, em, 0, fill_holes, 1, correct_aspect);
826
827         param_lscm_begin(handle, PARAM_FALSE, method == 0);
828         param_lscm_solve(handle);
829         param_lscm_end(handle);
830         
831         param_pack(handle, scene->toolsettings->uvcalc_margin);
832
833         param_flush(handle);
834
835         param_delete(handle);
836
837         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
838         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
839
840         BKE_mesh_end_editmesh(obedit->data, em);
841         return OPERATOR_FINISHED;
842 }
843
844 void UV_OT_unwrap(wmOperatorType *ot)
845 {
846         static EnumPropertyItem method_items[] = {
847                 {0, "ANGLE_BASED", 0, "Angle Based", ""},
848                 {1, "CONFORMAL", 0, "Conformal", ""},
849                 {0, NULL, 0, NULL, NULL}};
850
851         /* identifiers */
852         ot->name= "Unwrap";
853         ot->idname= "UV_OT_unwrap";
854         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
855         
856         /* api callbacks */
857         ot->exec= unwrap_exec;
858         ot->poll= ED_operator_uvmap;
859
860         /* properties */
861         RNA_def_enum(ot->srna, "method", method_items, 0, "Method", "Unwrapping method. Angle Based usually gives better results than Conformal, while being somewhat slower.");
862         RNA_def_boolean(ot->srna, "fill_holes", 1, "Fill Holes", "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry.");
863         RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UV's taking image aspect ratio into account.");
864 }
865
866 /**************** Project From View operator **************/
867 static int uv_from_view_exec(bContext *C, wmOperator *op)
868 {
869         Scene *scene= CTX_data_scene(C);
870         Object *obedit= CTX_data_edit_object(C);
871         Camera *camera= NULL;
872         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
873         ARegion *ar= CTX_wm_region(C);
874         View3D *v3d= CTX_wm_view3d(C);
875         RegionView3D *rv3d= ar->regiondata;
876         EditFace *efa;
877         MTFace *tf;
878         float rotmat[4][4];
879
880         /* add uvs if they don't exist yet */
881         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
882                 BKE_mesh_end_editmesh(obedit->data, em);
883                 return OPERATOR_CANCELLED;
884         }
885
886         /* establish the camera object, so we can default to view mapping if anything is wrong with it */
887         if ((rv3d->persp==RV3D_CAMOB) && (v3d->camera) && (v3d->camera->type==OB_CAMERA)) {
888                 camera= v3d->camera->data;
889         }
890
891         if(RNA_boolean_get(op->ptr, "orthographic")) {
892                 uv_map_rotation_matrix(rotmat, ar->regiondata, obedit, 90.0f, 0.0f, 1.0f);
893                 
894                 for(efa= em->faces.first; efa; efa= efa->next) {
895                         if(efa->f & SELECT) {
896                                 tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
897
898                                 project_from_view_ortho(tf->uv[0], efa->v1->co, rotmat);
899                                 project_from_view_ortho(tf->uv[1], efa->v2->co, rotmat);
900                                 project_from_view_ortho(tf->uv[2], efa->v3->co, rotmat);
901                                 if(efa->v4)
902                                         project_from_view_ortho(tf->uv[3], efa->v4->co, rotmat);
903                         }
904                 }
905         }
906         else if (camera) {
907                 struct UvCameraInfo *uci= project_camera_info(v3d->camera, obedit->obmat, scene->r.xsch, scene->r.ysch);
908                 
909                 if(uci) {
910                         for(efa= em->faces.first; efa; efa= efa->next) {
911                                 if(efa->f & SELECT) {
912                                         tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
913
914                                         project_from_camera(tf->uv[0], efa->v1->co, uci);
915                                         project_from_camera(tf->uv[1], efa->v2->co, uci);
916                                         project_from_camera(tf->uv[2], efa->v3->co, uci);
917                                         if(efa->v4)
918                                                 project_from_camera(tf->uv[3], efa->v4->co, uci);
919                                 }
920                         }
921                         
922                         MEM_freeN(uci);
923                 }
924         }
925         else {
926                 copy_m4_m4(rotmat, obedit->obmat);
927
928                 for(efa= em->faces.first; efa; efa= efa->next) {
929                         if(efa->f & SELECT) {
930                                 tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
931
932                                 project_from_view(tf->uv[0], efa->v1->co, rv3d->persmat, rotmat, ar->winx, ar->winy);
933                                 project_from_view(tf->uv[1], efa->v2->co, rv3d->persmat, rotmat, ar->winx, ar->winy);
934                                 project_from_view(tf->uv[2], efa->v3->co, rv3d->persmat, rotmat, ar->winx, ar->winy);
935                                 if(efa->v4)
936                                         project_from_view(tf->uv[3], efa->v4->co, rv3d->persmat, rotmat, ar->winx, ar->winy);
937                         }
938                 }
939         }
940
941         uv_map_clip_correct(em, op);
942
943         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
944         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
945
946         BKE_mesh_end_editmesh(obedit->data, em);
947         return OPERATOR_FINISHED;
948 }
949
950 static int uv_from_view_poll(bContext *C)
951 {
952         RegionView3D *rv3d= CTX_wm_region_view3d(C);
953
954         if(!ED_operator_uvmap(C))
955                 return 0;
956
957         return (rv3d != NULL);
958 }
959
960 void UV_OT_from_view(wmOperatorType *ot)
961 {
962         /* identifiers */
963         ot->name= "Project From View";
964         ot->idname= "UV_OT_project_from_view";
965         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
966         
967         /* api callbacks */
968         ot->exec= uv_from_view_exec;
969         ot->poll= uv_from_view_poll;
970
971         /* properties */
972         RNA_def_boolean(ot->srna, "orthographic", 0, "Orthographic", "Use orthographic projection.");
973         uv_map_clip_correct_properties(ot);
974 }
975
976 /********************** Reset operator ********************/
977
978 static int reset_exec(bContext *C, wmOperator *op)
979 {
980         Scene *scene= CTX_data_scene(C);
981         Object *obedit= CTX_data_edit_object(C);
982         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
983         EditFace *efa;
984         MTFace *tf;
985
986         /* add uvs if they don't exist yet */
987         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
988                 BKE_mesh_end_editmesh(obedit->data, em);
989                 return OPERATOR_CANCELLED;
990         }
991
992         for(efa= em->faces.first; efa; efa= efa->next) {
993                 if(efa->f & SELECT) {
994                         tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
995
996                         tf->uv[0][0]= 0.0f;
997                         tf->uv[0][1]= 0.0f;
998                         
999                         tf->uv[1][0]= 1.0f;
1000                         tf->uv[1][1]= 0.0f;
1001                         
1002                         tf->uv[2][0]= 1.0f;
1003                         tf->uv[2][1]= 1.0f;
1004                         
1005                         tf->uv[3][0]= 0.0f;
1006                         tf->uv[3][1]= 1.0f;
1007                 }
1008         }
1009
1010         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1011         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1012
1013         BKE_mesh_end_editmesh(obedit->data, em);
1014         return OPERATOR_FINISHED;
1015 }
1016
1017 void UV_OT_reset(wmOperatorType *ot)
1018 {
1019         /* identifiers */
1020         ot->name= "Reset";
1021         ot->idname= "UV_OT_reset";
1022         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1023         
1024         /* api callbacks */
1025         ot->exec= reset_exec;
1026         ot->poll= ED_operator_uvmap;
1027 }
1028
1029 /****************** Sphere Project operator ***************/
1030
1031 static void uv_sphere_project(float target[2], float source[3], float center[3], float rotmat[4][4])
1032 {
1033         float pv[3];
1034
1035         sub_v3_v3v3(pv, source, center);
1036         mul_m4_v3(rotmat, pv);
1037
1038         map_to_sphere( &target[0], &target[1],pv[0], pv[1], pv[2]);
1039
1040         /* split line is always zero */
1041         if(target[0] >= 1.0f)
1042                 target[0] -= 1.0f;  
1043 }
1044
1045 static void uv_map_mirror(EditFace *efa, MTFace *tf)
1046 {
1047         float dx;
1048         int nverts, i, mi;
1049
1050         nverts= (efa->v4)? 4: 3;
1051
1052         mi = 0;
1053         for(i=1; i<nverts; i++)
1054                 if(tf->uv[i][0] > tf->uv[mi][0])
1055                         mi = i;
1056
1057         for(i=0; i<nverts; i++) {
1058                 if(i != mi) {
1059                         dx = tf->uv[mi][0] - tf->uv[i][0];
1060                         if(dx > 0.5) tf->uv[i][0] += 1.0;
1061                 } 
1062         } 
1063 }
1064
1065 static int sphere_project_exec(bContext *C, wmOperator *op)
1066 {
1067         Scene *scene= CTX_data_scene(C);
1068         Object *obedit= CTX_data_edit_object(C);
1069         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
1070         EditFace *efa;
1071         MTFace *tf;
1072         float center[3], rotmat[4][4];
1073
1074         /* add uvs if they don't exist yet */
1075         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1076                 BKE_mesh_end_editmesh(obedit->data, em);
1077                 return OPERATOR_CANCELLED;
1078         }
1079
1080         uv_map_transform(C, op, center, rotmat);
1081
1082         for(efa= em->faces.first; efa; efa= efa->next) {
1083                 if(efa->f & SELECT) {
1084                         tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1085
1086                         uv_sphere_project(tf->uv[0], efa->v1->co, center, rotmat);
1087                         uv_sphere_project(tf->uv[1], efa->v2->co, center, rotmat);
1088                         uv_sphere_project(tf->uv[2], efa->v3->co, center, rotmat);
1089                         if(efa->v4)
1090                                 uv_sphere_project(tf->uv[3], efa->v4->co, center, rotmat);
1091
1092                         uv_map_mirror(efa, tf);
1093                 }
1094         }
1095
1096         uv_map_clip_correct(em, op);
1097
1098         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1099         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1100
1101         BKE_mesh_end_editmesh(obedit->data, em);
1102         return OPERATOR_FINISHED;
1103 }
1104
1105 void UV_OT_sphere_project(wmOperatorType *ot)
1106 {
1107         /* identifiers */
1108         ot->name= "Sphere Projection";
1109         ot->idname= "UV_OT_sphere_project";
1110         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1111         
1112         /* api callbacks */
1113         ot->exec= sphere_project_exec;
1114         ot->poll= ED_operator_uvmap;
1115
1116         /* properties */
1117         uv_transform_properties(ot, 0);
1118         uv_map_clip_correct_properties(ot);
1119 }
1120
1121 /***************** Cylinder Project operator **************/
1122
1123 static void uv_cylinder_project(float target[2], float source[3], float center[3], float rotmat[4][4])
1124 {
1125         float pv[3];
1126
1127         sub_v3_v3v3(pv, source, center);
1128         mul_m4_v3(rotmat, pv);
1129
1130         map_to_tube( &target[0], &target[1],pv[0], pv[1], pv[2]);
1131
1132         /* split line is always zero */
1133         if(target[0] >= 1.0f)
1134                 target[0] -= 1.0f;  
1135 }
1136
1137 static int cylinder_project_exec(bContext *C, wmOperator *op)
1138 {
1139         Scene *scene= CTX_data_scene(C);
1140         Object *obedit= CTX_data_edit_object(C);
1141         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
1142         EditFace *efa;
1143         MTFace *tf;
1144         float center[3], rotmat[4][4];
1145
1146         /* add uvs if they don't exist yet */
1147         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1148                 BKE_mesh_end_editmesh(obedit->data, em);
1149                 return OPERATOR_CANCELLED;
1150         }
1151
1152         uv_map_transform(C, op, center, rotmat);
1153
1154         for(efa= em->faces.first; efa; efa= efa->next) {
1155                 if(efa->f & SELECT) {
1156                         tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1157
1158                         uv_cylinder_project(tf->uv[0], efa->v1->co, center, rotmat);
1159                         uv_cylinder_project(tf->uv[1], efa->v2->co, center, rotmat);
1160                         uv_cylinder_project(tf->uv[2], efa->v3->co, center, rotmat);
1161                         if(efa->v4)
1162                                 uv_cylinder_project(tf->uv[3], efa->v4->co, center, rotmat);
1163
1164                         uv_map_mirror(efa, tf);
1165                 }
1166         }
1167
1168         uv_map_clip_correct(em, op);
1169
1170         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1171         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1172
1173         BKE_mesh_end_editmesh(obedit->data, em);
1174         return OPERATOR_FINISHED;
1175 }
1176
1177 void UV_OT_cylinder_project(wmOperatorType *ot)
1178 {
1179         /* identifiers */
1180         ot->name= "Cylinder Projection";
1181         ot->idname= "UV_OT_cylinder_project";
1182         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1183         
1184         /* api callbacks */
1185         ot->exec= cylinder_project_exec;
1186         ot->poll= ED_operator_uvmap;
1187
1188         /* properties */
1189         uv_transform_properties(ot, 1);
1190         uv_map_clip_correct_properties(ot);
1191 }
1192
1193 /******************* Cube Project operator ****************/
1194
1195 static int cube_project_exec(bContext *C, wmOperator *op)
1196 {
1197         Scene *scene= CTX_data_scene(C);
1198         Object *obedit= CTX_data_edit_object(C);
1199         EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
1200         EditFace *efa;
1201         MTFace *tf;
1202         float no[3], cube_size, *loc, dx, dy;
1203         int cox, coy;
1204
1205         /* add uvs if they don't exist yet */
1206         if(!ED_uvedit_ensure_uvs(C, scene, obedit)) {
1207                 BKE_mesh_end_editmesh(obedit->data, em);
1208                 return OPERATOR_CANCELLED;
1209         }
1210
1211         loc= obedit->obmat[3];
1212         cube_size= RNA_float_get(op->ptr, "cube_size");
1213
1214         /* choose x,y,z axis for projection depending on the largest normal
1215          * component, but clusters all together around the center of map. */
1216
1217         for(efa= em->faces.first; efa; efa= efa->next) {
1218                 if(efa->f & SELECT) {
1219                         tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
1220                         normal_tri_v3( no,efa->v1->co, efa->v2->co, efa->v3->co);
1221                         
1222                         no[0]= fabs(no[0]);
1223                         no[1]= fabs(no[1]);
1224                         no[2]= fabs(no[2]);
1225                         
1226                         cox=0; coy= 1;
1227                         if(no[2]>=no[0] && no[2]>=no[1]);
1228                         else if(no[1]>=no[0] && no[1]>=no[2]) coy= 2;
1229                         else { cox= 1; coy= 2; }
1230                         
1231                         tf->uv[0][0]= 0.5+0.5*cube_size*(loc[cox] + efa->v1->co[cox]);
1232                         tf->uv[0][1]= 0.5+0.5*cube_size*(loc[coy] + efa->v1->co[coy]);
1233                         dx = floor(tf->uv[0][0]);
1234                         dy = floor(tf->uv[0][1]);
1235                         tf->uv[0][0] -= dx;
1236                         tf->uv[0][1] -= dy;
1237                         tf->uv[1][0]= 0.5+0.5*cube_size*(loc[cox] + efa->v2->co[cox]);
1238                         tf->uv[1][1]= 0.5+0.5*cube_size*(loc[coy] + efa->v2->co[coy]);
1239                         tf->uv[1][0] -= dx;
1240                         tf->uv[1][1] -= dy;
1241                         tf->uv[2][0]= 0.5+0.5*cube_size*(loc[cox] + efa->v3->co[cox]);
1242                         tf->uv[2][1]= 0.5+0.5*cube_size*(loc[coy] + efa->v3->co[coy]);
1243                         tf->uv[2][0] -= dx;
1244                         tf->uv[2][1] -= dy;
1245
1246                         if(efa->v4) {
1247                                 tf->uv[3][0]= 0.5+0.5*cube_size*(loc[cox] + efa->v4->co[cox]);
1248                                 tf->uv[3][1]= 0.5+0.5*cube_size*(loc[coy] + efa->v4->co[coy]);
1249                                 tf->uv[3][0] -= dx;
1250                                 tf->uv[3][1] -= dy;
1251                         }
1252                 }
1253         }
1254
1255         uv_map_clip_correct(em, op);
1256
1257         DAG_id_flush_update(obedit->data, OB_RECALC_DATA);
1258         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1259
1260         BKE_mesh_end_editmesh(obedit->data, em);
1261         return OPERATOR_FINISHED;
1262 }
1263
1264 void UV_OT_cube_project(wmOperatorType *ot)
1265 {
1266         /* identifiers */
1267         ot->name= "Cube Projection";
1268         ot->idname= "UV_OT_cube_project";
1269         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1270         
1271         /* api callbacks */
1272         ot->exec= cube_project_exec;
1273         ot->poll= ED_operator_uvmap;
1274
1275         /* properties */
1276         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);
1277         uv_map_clip_correct_properties(ot);
1278 }