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