Fix #32450: edge slide with multiple loops selected could move some loops
[blender.git] / source / blender / editors / transform / transform.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/transform/transform.c
29  *  \ingroup edtransform
30  */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <math.h>
36 #include <float.h>
37
38 #ifndef WIN32
39 #  include <unistd.h>
40 #else
41 #  include <io.h>
42 #endif
43
44 #include "MEM_guardedalloc.h"
45
46 #include "DNA_anim_types.h"
47 #include "DNA_armature_types.h"
48 #include "DNA_constraint_types.h"
49 #include "DNA_mesh_types.h"
50 #include "DNA_meshdata_types.h"
51 #include "DNA_mask_types.h"
52 #include "DNA_movieclip_types.h"
53 #include "DNA_scene_types.h"  /* PET modes */
54
55 #include "RNA_access.h"
56
57 #include "BIF_gl.h"
58 #include "BIF_glutil.h"
59
60 #include "BKE_nla.h"
61 #include "BKE_bmesh.h"
62 #include "BKE_context.h"
63 #include "BKE_constraint.h"
64 #include "BKE_global.h"
65 #include "BKE_particle.h"
66 #include "BKE_pointcache.h"
67 #include "BKE_unit.h"
68
69 #include "ED_image.h"
70 #include "ED_keyframing.h"
71 #include "ED_screen.h"
72 #include "ED_space_api.h"
73 #include "ED_markers.h"
74 #include "ED_view3d.h"
75 #include "ED_mesh.h"
76 #include "ED_clip.h"
77 #include "ED_mask.h"
78
79 #include "UI_view2d.h"
80 #include "WM_types.h"
81 #include "WM_api.h"
82
83 #include "BLI_math.h"
84 #include "BLI_blenlib.h"
85 #include "BLI_utildefines.h"
86 #include "BLI_ghash.h"
87 #include "BLI_linklist.h"
88 #include "BLI_smallhash.h"
89 #include "BLI_array.h"
90
91 #include "UI_resources.h"
92
93 #include "transform.h"
94
95 #include <stdio.h>
96
97 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
98 static int doEdgeSlide(TransInfo *t, float perc);
99
100 /* ************************** SPACE DEPENDANT CODE **************************** */
101
102 void setTransformViewMatrices(TransInfo *t)
103 {
104         if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
105                 RegionView3D *rv3d = t->ar->regiondata;
106
107                 copy_m4_m4(t->viewmat, rv3d->viewmat);
108                 copy_m4_m4(t->viewinv, rv3d->viewinv);
109                 copy_m4_m4(t->persmat, rv3d->persmat);
110                 copy_m4_m4(t->persinv, rv3d->persinv);
111                 t->persp = rv3d->persp;
112         }
113         else {
114                 unit_m4(t->viewmat);
115                 unit_m4(t->viewinv);
116                 unit_m4(t->persmat);
117                 unit_m4(t->persinv);
118                 t->persp = RV3D_ORTHO;
119         }
120
121         calculateCenter2D(t);
122 }
123
124 static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
125 {
126         float divx, divy;
127         
128         divx = BLI_RCT_SIZE_X(&v2d->mask);
129         divy = BLI_RCT_SIZE_Y(&v2d->mask);
130
131         r_vec[0] = BLI_RCT_SIZE_X(&v2d->cur) * dx / divx;
132         r_vec[1] = BLI_RCT_SIZE_Y(&v2d->cur) * dy / divy;
133         r_vec[2] = 0.0f;
134 }
135
136 static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
137 {
138         float divx, divy;
139         float mulx, muly;
140
141         divx = BLI_RCT_SIZE_X(&v2d->mask);
142         divy = BLI_RCT_SIZE_Y(&v2d->mask);
143
144         mulx = BLI_RCT_SIZE_X(&v2d->cur);
145         muly = BLI_RCT_SIZE_Y(&v2d->cur);
146
147         /* difference with convertViewVec2D */
148         /* clamp w/h, mask only */
149         if (mulx / divx < muly / divy) {
150                 divy = divx;
151                 muly = mulx;
152         }
153         else {
154                 divx = divy;
155                 mulx = muly;
156         }
157         /* end difference */
158
159         r_vec[0] = mulx * dx / divx;
160         r_vec[1] = muly * dy / divy;
161         r_vec[2] = 0.0f;
162 }
163
164 void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy)
165 {
166         if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
167                 float mval_f[2];
168                 mval_f[0] = dx;
169                 mval_f[1] = dy;
170                 ED_view3d_win_to_delta(t->ar, mval_f, r_vec);
171         }
172         else if (t->spacetype == SPACE_IMAGE) {
173                 float aspx, aspy;
174
175                 if (t->options & CTX_MASK) {
176
177                         convertViewVec2D_mask(t->view, r_vec, dx, dy);
178                         ED_space_image_get_aspect(t->sa->spacedata.first, &aspx, &aspy);
179                 }
180                 else {
181                         convertViewVec2D(t->view, r_vec, dx, dy);
182                         ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
183                 }
184
185                 r_vec[0] *= aspx;
186                 r_vec[1] *= aspy;
187         }
188         else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
189                 convertViewVec2D(t->view, r_vec, dx, dy);
190         }
191         else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
192                 convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
193         }
194         else if (t->spacetype == SPACE_CLIP) {
195                 float aspx, aspy;
196
197                 if (t->options & CTX_MASK) {
198                         convertViewVec2D_mask(t->view, r_vec, dx, dy);
199                 }
200                 else {
201                         convertViewVec2D(t->view, r_vec, dx, dy);
202                 }
203
204                 if (t->options & CTX_MOVIECLIP) {
205                         ED_space_clip_get_aspect_dimension_aware(t->sa->spacedata.first, &aspx, &aspy);
206                 }
207                 else if (t->options & CTX_MASK) {
208                         /* TODO - NOT WORKING, this isnt so bad since its only display aspect */
209                         ED_space_clip_get_aspect(t->sa->spacedata.first, &aspx, &aspy);
210                 }
211                 else {
212                         /* should never happen, quiet warnings */
213                         BLI_assert(0);
214                         aspx = aspy = 1.0f;
215                 }
216
217                 r_vec[0] *= aspx;
218                 r_vec[1] *= aspy;
219         }
220         else {
221                 printf("%s: called in an invalid context\n", __func__);
222                 zero_v3(r_vec);
223         }
224 }
225
226 void projectIntView(TransInfo *t, const float vec[3], int adr[2])
227 {
228         if (t->spacetype == SPACE_VIEW3D) {
229                 if (t->ar->regiontype == RGN_TYPE_WINDOW)
230                         project_int_noclip(t->ar, vec, adr);
231         }
232         else if (t->spacetype == SPACE_IMAGE) {
233                 if (t->options & CTX_MASK) {
234                         float v[2];
235                         ED_mask_point_pos__reverse(t->sa, t->ar, vec[0], vec[1], &v[0], &v[1]);
236                         adr[0] = v[0];
237                         adr[1] = v[1];
238                 }
239                 else {
240                         float aspx, aspy, v[2];
241
242                         ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
243                         v[0] = vec[0] / aspx;
244                         v[1] = vec[1] / aspy;
245
246                         UI_view2d_to_region_no_clip(t->view, v[0], v[1], adr, adr + 1);
247                 }
248         }
249         else if (t->spacetype == SPACE_ACTION) {
250                 int out[2] = {0, 0};
251 #if 0
252                 SpaceAction *sact = t->sa->spacedata.first;
253
254                 if (sact->flag & SACTION_DRAWTIME) {
255                         //vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
256                         /* same as below */
257                         UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], out, out + 1);
258                 } 
259                 else
260 #endif
261                 {
262                         UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], out, out + 1);
263                 }
264
265                 adr[0] = out[0];
266                 adr[1] = out[1];
267         }
268         else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
269                 int out[2] = {0, 0};
270
271                 UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], out, out + 1);
272                 adr[0] = out[0];
273                 adr[1] = out[1];
274         }
275         else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
276                 int out[2] = {0, 0};
277
278                 UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], out, out + 1);
279                 adr[0] = out[0];
280                 adr[1] = out[1];
281         }
282         else if (t->spacetype == SPACE_CLIP) {
283                 float v[2];
284                 float aspx = 1.0f, aspy = 1.0f;
285
286                 copy_v2_v2(v, vec);
287
288                 if (t->options & CTX_MOVIECLIP) {
289                         ED_space_clip_get_aspect_dimension_aware(t->sa->spacedata.first, &aspx, &aspy);
290                 }
291                 else if (t->options & CTX_MASK) {
292                         /* MASKTODO - not working as expected */
293                         ED_space_clip_get_aspect(t->sa->spacedata.first, &aspx, &aspy);
294                 }
295
296                 v[0] /= aspx;
297                 v[1] /= aspy;
298
299                 UI_view2d_to_region_no_clip(t->view, v[0], v[1], adr, adr + 1);
300         }
301         else if (t->spacetype == SPACE_NODE) {
302                 UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], adr, adr + 1);
303         }
304 }
305
306 void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
307 {
308         switch (t->spacetype) {
309                 case SPACE_VIEW3D:
310                 {
311                         if (t->ar->regiontype == RGN_TYPE_WINDOW) {
312                                 project_float_noclip(t->ar, vec, adr);
313                                 return;
314                         }
315                         break;
316                 }
317                 case SPACE_IMAGE:
318                 case SPACE_CLIP:
319                 case SPACE_IPO:
320                 case SPACE_NLA:
321                 {
322                         int a[2];
323                         projectIntView(t, vec, a);
324                         adr[0] = a[0];
325                         adr[1] = a[1];
326                         return;
327                 }
328         }
329
330         zero_v2(adr);
331 }
332
333 void applyAspectRatio(TransInfo *t, float vec[2])
334 {
335         if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
336                 SpaceImage *sima = t->sa->spacedata.first;
337                 float aspx, aspy;
338
339                 if ((sima->flag & SI_COORDFLOATS) == 0) {
340                         int width, height;
341                         ED_space_image_get_size(sima, &width, &height);
342
343                         vec[0] *= width;
344                         vec[1] *= height;
345                 }
346
347                 ED_space_image_get_uv_aspect(sima, &aspx, &aspy);
348                 vec[0] /= aspx;
349                 vec[1] /= aspy;
350         }
351         else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
352                 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
353                         SpaceClip *sc = t->sa->spacedata.first;
354                         float aspx, aspy;
355
356
357                         if (t->options & CTX_MOVIECLIP) {
358                                 ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy);
359
360                                 vec[0] /= aspx;
361                                 vec[1] /= aspy;
362                         }
363                         else if (t->options & CTX_MASK) {
364                                 ED_space_clip_get_aspect(sc, &aspx, &aspy);
365
366                                 vec[0] /= aspx;
367                                 vec[1] /= aspy;
368                         }
369                 }
370         }
371 }
372
373 void removeAspectRatio(TransInfo *t, float vec[2])
374 {
375         if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
376                 SpaceImage *sima = t->sa->spacedata.first;
377                 float aspx, aspy;
378
379                 if ((sima->flag & SI_COORDFLOATS) == 0) {
380                         int width, height;
381                         ED_space_image_get_size(sima, &width, &height);
382
383                         vec[0] /= width;
384                         vec[1] /= height;
385                 }
386
387                 ED_space_image_get_uv_aspect(sima, &aspx, &aspy);
388                 vec[0] *= aspx;
389                 vec[1] *= aspy;
390         }
391         else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
392                 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
393                         SpaceClip *sc = t->sa->spacedata.first;
394                         float aspx = 1.0f, aspy = 1.0f;
395
396                         if (t->options & CTX_MOVIECLIP) {
397                                 ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy);
398                         }
399                         else if (t->options & CTX_MASK) {
400                                 ED_space_clip_get_aspect(sc, &aspx, &aspy);
401                         }
402
403                         vec[0] *= aspx;
404                         vec[1] *= aspy;
405                 }
406         }
407 }
408
409 static void viewRedrawForce(const bContext *C, TransInfo *t)
410 {
411         if (t->spacetype == SPACE_VIEW3D) {
412                 /* Do we need more refined tags? */
413                 if (t->flag & T_POSE)
414                         WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
415                 else
416                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
417                 
418                 /* for realtime animation record - send notifiers recognised by animation editors */
419                 // XXX: is this notifier a lame duck?
420                 if ((t->animtimer) && IS_AUTOKEY_ON(t->scene))
421                         WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
422                 
423         }
424         else if (t->spacetype == SPACE_ACTION) {
425                 //SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first;
426                 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
427         }
428         else if (t->spacetype == SPACE_IPO) {
429                 //SpaceIpo *sipo= (SpaceIpo *)t->sa->spacedata.first;
430                 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
431         }
432         else if (t->spacetype == SPACE_NLA) {
433                 WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
434         }
435         else if (t->spacetype == SPACE_NODE) {
436                 //ED_area_tag_redraw(t->sa);
437                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
438         }
439         else if (t->spacetype == SPACE_SEQ) {
440                 WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
441         }
442         else if (t->spacetype == SPACE_IMAGE) {
443                 if (t->options & CTX_MASK) {
444                         Mask *mask = CTX_data_edit_mask(C);
445
446                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
447                 }
448                 else {
449                         // XXX how to deal with lock?
450                         SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
451                         if (sima->lock) WM_event_add_notifier(C, NC_GEOM | ND_DATA, t->obedit->data);
452                         else ED_area_tag_redraw(t->sa);
453                 }
454         }
455         else if (t->spacetype == SPACE_CLIP) {
456                 SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
457
458                 if (ED_space_clip_check_show_trackedit(sc)) {
459                         MovieClip *clip = ED_space_clip_get_clip(sc);
460
461                         /* objects could be parented to tracking data, so send this for viewport refresh */
462                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
463
464                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
465                 }
466                 else if (ED_space_clip_check_show_maskedit(sc)) {
467                         Mask *mask = CTX_data_edit_mask(C);
468
469                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
470                 }
471         }
472 }
473
474 static void viewRedrawPost(bContext *C, TransInfo *t)
475 {
476         ED_area_headerprint(t->sa, NULL);
477         
478         if (t->spacetype == SPACE_VIEW3D) {
479                 /* if autokeying is enabled, send notifiers that keyframes were added */
480                 if (IS_AUTOKEY_ON(t->scene))
481                         WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
482                 
483                 /* XXX temp, first hack to get auto-render in compositor work (ton) */
484                 WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
485
486         }
487         
488 #if 0 // TRANSFORM_FIX_ME
489         if (t->spacetype == SPACE_VIEW3D) {
490                 allqueue(REDRAWBUTSOBJECT, 0);
491                 allqueue(REDRAWVIEW3D, 0);
492         }
493         else if (t->spacetype == SPACE_IMAGE) {
494                 allqueue(REDRAWIMAGE, 0);
495                 allqueue(REDRAWVIEW3D, 0);
496         }
497         else if (ELEM3(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) {
498                 allqueue(REDRAWVIEW3D, 0);
499                 allqueue(REDRAWACTION, 0);
500                 allqueue(REDRAWNLA, 0);
501                 allqueue(REDRAWIPO, 0);
502                 allqueue(REDRAWTIME, 0);
503                 allqueue(REDRAWBUTSOBJECT, 0);
504         }
505
506         scrarea_queue_headredraw(curarea);
507 #endif
508 }
509
510 /* ************************** TRANSFORMATIONS **************************** */
511
512 void BIF_selectOrientation(void)
513 {
514 #if 0 // TRANSFORM_FIX_ME
515         short val;
516         char *str_menu = BIF_menustringTransformOrientation("Orientation");
517         val = pupmenu(str_menu);
518         MEM_freeN(str_menu);
519
520         if (val >= 0) {
521                 G.vd->twmode = val;
522         }
523 #endif
524 }
525
526 static void view_editmove(unsigned short UNUSED(event))
527 {
528 #if 0 // TRANSFORM_FIX_ME
529         int refresh = 0;
530         /* Regular:   Zoom in */
531         /* Shift:     Scroll up */
532         /* Ctrl:      Scroll right */
533         /* Alt-Shift: Rotate up */
534         /* Alt-Ctrl:  Rotate right */
535
536         /* only work in 3D window for now
537          * In the end, will have to send to event to a 2D window handler instead
538          */
539         if (Trans.flag & T_2D_EDIT)
540                 return;
541
542         switch (event) {
543                 case WHEELUPMOUSE:
544
545                         if (G.qual & LR_SHIFTKEY) {
546                                 if (G.qual & LR_ALTKEY) {
547                                         G.qual &= ~LR_SHIFTKEY;
548                                         persptoetsen(PAD2);
549                                         G.qual |= LR_SHIFTKEY;
550                                 }
551                                 else {
552                                         persptoetsen(PAD2);
553                                 }
554                         }
555                         else if (G.qual & LR_CTRLKEY) {
556                                 if (G.qual & LR_ALTKEY) {
557                                         G.qual &= ~LR_CTRLKEY;
558                                         persptoetsen(PAD4);
559                                         G.qual |= LR_CTRLKEY;
560                                 }
561                                 else {
562                                         persptoetsen(PAD4);
563                                 }
564                         }
565                         else if (U.uiflag & USER_WHEELZOOMDIR)
566                                 persptoetsen(PADMINUS);
567                         else
568                                 persptoetsen(PADPLUSKEY);
569
570                         refresh = 1;
571                         break;
572                 case WHEELDOWNMOUSE:
573                         if (G.qual & LR_SHIFTKEY) {
574                                 if (G.qual & LR_ALTKEY) {
575                                         G.qual &= ~LR_SHIFTKEY;
576                                         persptoetsen(PAD8);
577                                         G.qual |= LR_SHIFTKEY;
578                                 }
579                                 else {
580                                         persptoetsen(PAD8);
581                                 }
582                         }
583                         else if (G.qual & LR_CTRLKEY) {
584                                 if (G.qual & LR_ALTKEY) {
585                                         G.qual &= ~LR_CTRLKEY;
586                                         persptoetsen(PAD6);
587                                         G.qual |= LR_CTRLKEY;
588                                 }
589                                 else {
590                                         persptoetsen(PAD6);
591                                 }
592                         }
593                         else if (U.uiflag & USER_WHEELZOOMDIR)
594                                 persptoetsen(PADPLUSKEY);
595                         else
596                                 persptoetsen(PADMINUS);
597
598                         refresh = 1;
599                         break;
600         }
601
602         if (refresh)
603                 setTransformViewMatrices(&Trans);
604 #endif
605 }
606
607 /* ************************************************* */
608
609 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
610 #define TFM_MODAL_CANCEL        1
611 #define TFM_MODAL_CONFIRM       2
612 #define TFM_MODAL_TRANSLATE     3
613 #define TFM_MODAL_ROTATE        4
614 #define TFM_MODAL_RESIZE        5
615 #define TFM_MODAL_SNAP_INV_ON   6
616 #define TFM_MODAL_SNAP_INV_OFF  7
617 #define TFM_MODAL_SNAP_TOGGLE   8
618 #define TFM_MODAL_AXIS_X        9
619 #define TFM_MODAL_AXIS_Y        10
620 #define TFM_MODAL_AXIS_Z        11
621 #define TFM_MODAL_PLANE_X       12
622 #define TFM_MODAL_PLANE_Y       13
623 #define TFM_MODAL_PLANE_Z       14
624 #define TFM_MODAL_CONS_OFF      15
625 #define TFM_MODAL_ADD_SNAP      16
626 #define TFM_MODAL_REMOVE_SNAP   17
627 /*      18 and 19 used by numinput, defined in transform.h
628  * */
629 #define TFM_MODAL_PROPSIZE_UP   20
630 #define TFM_MODAL_PROPSIZE_DOWN 21
631 #define TFM_MODAL_AUTOIK_LEN_INC 22
632 #define TFM_MODAL_AUTOIK_LEN_DEC 23
633
634 #define TFM_MODAL_EDGESLIDE_UP 24
635 #define TFM_MODAL_EDGESLIDE_DOWN 25
636
637 /* called in transform_ops.c, on each regeneration of keymaps */
638 wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
639 {
640         static EnumPropertyItem modal_items[] = {
641                 {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
642                 {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
643                 {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Translate", ""},
644                 {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
645                 {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
646                 {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Invert Snap On", ""},
647                 {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Invert Snap Off", ""},
648                 {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
649                 {TFM_MODAL_AXIS_X, "AXIS_X", 0, "Orientation X axis", ""},
650                 {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Orientation Y axis", ""},
651                 {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Orientation Z axis", ""},
652                 {TFM_MODAL_PLANE_X, "PLANE_X", 0, "Orientation X plane", ""},
653                 {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Orientation Y plane", ""},
654                 {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Orientation Z plane", ""},
655                 {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Remove Constraints", ""},
656                 {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
657                 {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
658                 {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
659                 {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
660                 {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
661                 {TFM_MODAL_PROPSIZE_DOWN, "PROPORTIONAL_SIZE_DOWN", 0, "Decrease Proportional Influence", ""},
662                 {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
663                 {TFM_MODAL_AUTOIK_LEN_DEC, "AUTOIK_CHAIN_LEN_DOWN", 0, "Decrease Max AutoIK Chain Length", ""},
664                 {TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
665                 {TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
666                 {0, NULL, 0, NULL, NULL}
667         };
668         
669         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
670         
671         /* this function is called for each spacetype, only needs to add map once */
672         if (keymap && keymap->modal_items) return NULL;
673         
674         keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
675         
676         /* items for modal map */
677         WM_modalkeymap_add_item(keymap, ESCKEY,    KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL);
678         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
679         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
680         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
681
682         WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE);
683         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE);
684         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, TFM_MODAL_RESIZE);
685         
686         WM_modalkeymap_add_item(keymap, TABKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_SNAP_TOGGLE);
687
688         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
689         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
690
691         WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
692         WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
693         
694         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, TFM_MODAL_ADD_SNAP);
695         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, KM_ALT, 0, TFM_MODAL_REMOVE_SNAP);
696
697         WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
698         WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
699         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
700         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
701
702         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_UP);
703         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_DOWN);
704         
705         WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
706         WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
707         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
708         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
709         
710         return keymap;
711 }
712
713 static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode)
714 {
715         if (!(t->flag & T_NO_CONSTRAINT)) {
716                 int constraint_axis, constraint_plane;
717                 int edit_2d = (t->flag & T_2D_EDIT);
718                 char msg1[] = "along _";
719                 char msg2[] = "along %s _";
720                 char msg3[] = "locking %s _";
721                 char axis;
722         
723                 /* Initialize */
724                 switch (key_type) {
725                         case XKEY:
726                                 axis = 'X';
727                                 constraint_axis = CON_AXIS0;
728                                 break;
729                         case YKEY:
730                                 axis = 'Y';
731                                 constraint_axis = CON_AXIS1;
732                                 break;
733                         case ZKEY:
734                                 axis = 'Z';
735                                 constraint_axis = CON_AXIS2;
736                                 break;
737                         default:
738                                 /* Invalid key */
739                                 return;
740                 }
741                 msg1[sizeof(msg1) - 2] = axis;
742                 msg2[sizeof(msg2) - 2] = axis;
743                 msg3[sizeof(msg3) - 2] = axis;
744                 constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
745
746                 if (edit_2d && (key_type != ZKEY)) {
747                         if (cmode == axis) {
748                                 stopConstraint(t);
749                         }
750                         else {
751                                 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg1);
752                         }
753                 }
754                 else if (!edit_2d) {
755                         if (cmode == axis) {
756                                 if (t->con.orientation != V3D_MANIP_GLOBAL) {
757                                         stopConstraint(t);
758                                 }
759                                 else {
760                                         short orientation = (t->current_orientation != V3D_MANIP_GLOBAL ?
761                                                              t->current_orientation : V3D_MANIP_LOCAL);
762                                         if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
763                                                 setUserConstraint(t, orientation, constraint_axis, msg2);
764                                         else if (t->modifiers & MOD_CONSTRAINT_PLANE)
765                                                 setUserConstraint(t, orientation, constraint_plane, msg3);
766                                 }
767                         }
768                         else {
769                                 if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
770                                         setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg2);
771                                 else if (t->modifiers & MOD_CONSTRAINT_PLANE)
772                                         setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_plane, msg3);
773                         }
774                 }
775                 t->redraw |= TREDRAW_HARD;
776         }
777 }
778
779 int transformEvent(TransInfo *t, wmEvent *event)
780 {
781         float mati[3][3] = MAT3_UNITY;
782         char cmode = constraintModeToChar(t);
783         int handled = 1;
784
785         t->redraw |= handleMouseInput(t, &t->mouse, event);
786
787         if (event->type == MOUSEMOVE) {
788                 if (t->modifiers & MOD_CONSTRAINT_SELECT)
789                         t->con.mode |= CON_SELECT;
790
791                 copy_v2_v2_int(t->mval, event->mval);
792
793                 // t->redraw |= TREDRAW_SOFT; /* Use this for soft redraw. Might cause flicker in object mode */
794                 t->redraw |= TREDRAW_HARD;
795
796
797                 if (t->state == TRANS_STARTING) {
798                         t->state = TRANS_RUNNING;
799                 }
800
801                 applyMouseInput(t, &t->mouse, t->mval, t->values);
802
803                 // Snapping mouse move events
804                 t->redraw |= handleSnapping(t, event);
805         }
806
807         /* handle modal keymap first */
808         if (event->type == EVT_MODAL_MAP) {
809                 switch (event->val) {
810                         case TFM_MODAL_CANCEL:
811                                 t->state = TRANS_CANCEL;
812                                 break;
813                         case TFM_MODAL_CONFIRM:
814                                 t->state = TRANS_CONFIRM;
815                                 break;
816                         case TFM_MODAL_TRANSLATE:
817                                 /* only switch when... */
818                                 if (ELEM3(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL) ) {
819                                         resetTransRestrictions(t);
820                                         restoreTransObjects(t);
821                                         initTranslation(t);
822                                         initSnapping(t, NULL); // need to reinit after mode change
823                                         t->redraw |= TREDRAW_HARD;
824                                 }
825                                 else if (t->mode == TFM_TRANSLATION) {
826                                         if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
827                                                 restoreTransObjects(t);
828
829                                                 t->flag ^= T_ALT_TRANSFORM;
830                                                 t->redraw |= TREDRAW_HARD;
831                                         }
832                                 }
833                                 break;
834                         case TFM_MODAL_ROTATE:
835                                 /* only switch when... */
836                                 if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
837                                         if (ELEM4(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION) ) {
838                                                 
839                                                 resetTransRestrictions(t);
840                                                 
841                                                 if (t->mode == TFM_ROTATION) {
842                                                         restoreTransObjects(t);
843                                                         initTrackball(t);
844                                                 }
845                                                 else {
846                                                         restoreTransObjects(t);
847                                                         initRotation(t);
848                                                 }
849                                                 initSnapping(t, NULL); // need to reinit after mode change
850                                                 t->redraw |= TREDRAW_HARD;
851                                         }
852                                 }
853                                 break;
854                         case TFM_MODAL_RESIZE:
855                                 /* only switch when... */
856                                 if (ELEM3(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL) ) {
857                                         resetTransRestrictions(t);
858                                         restoreTransObjects(t);
859                                         initResize(t);
860                                         initSnapping(t, NULL); // need to reinit after mode change
861                                         t->redraw |= TREDRAW_HARD;
862                                 }
863                                 else if (t->mode == TFM_RESIZE) {
864                                         if (t->options & CTX_MOVIECLIP) {
865                                                 restoreTransObjects(t);
866
867                                                 t->flag ^= T_ALT_TRANSFORM;
868                                                 t->redraw |= TREDRAW_HARD;
869                                         }
870                                 }
871                                 break;
872                                 
873                         case TFM_MODAL_SNAP_INV_ON:
874                                 t->modifiers |= MOD_SNAP_INVERT;
875                                 t->redraw |= TREDRAW_HARD;
876                                 break;
877                         case TFM_MODAL_SNAP_INV_OFF:
878                                 t->modifiers &= ~MOD_SNAP_INVERT;
879                                 t->redraw |= TREDRAW_HARD;
880                                 break;
881                         case TFM_MODAL_SNAP_TOGGLE:
882                                 t->modifiers ^= MOD_SNAP;
883                                 t->redraw |= TREDRAW_HARD;
884                                 break;
885                         case TFM_MODAL_AXIS_X:
886                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
887                                         if (cmode == 'X') {
888                                                 stopConstraint(t);
889                                         }
890                                         else {
891                                                 if (t->flag & T_2D_EDIT) {
892                                                         setUserConstraint(t, V3D_MANIP_GLOBAL, (CON_AXIS0), "along X");
893                                                 }
894                                                 else {
895                                                         setUserConstraint(t, t->current_orientation, (CON_AXIS0), "along %s X");
896                                                 }
897                                         }
898                                         t->redraw |= TREDRAW_HARD;
899                                 }
900                                 break;
901                         case TFM_MODAL_AXIS_Y:
902                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
903                                         if (cmode == 'Y') {
904                                                 stopConstraint(t);
905                                         }
906                                         else {
907                                                 if (t->flag & T_2D_EDIT) {
908                                                         setUserConstraint(t, V3D_MANIP_GLOBAL, (CON_AXIS1), "along Y");
909                                                 }
910                                                 else {
911                                                         setUserConstraint(t, t->current_orientation, (CON_AXIS1), "along %s Y");
912                                                 }
913                                         }
914                                         t->redraw |= TREDRAW_HARD;
915                                 }
916                                 break;
917                         case TFM_MODAL_AXIS_Z:
918                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
919                                         if (cmode == 'Z') {
920                                                 stopConstraint(t);
921                                         }
922                                         else {
923                                                 setUserConstraint(t, t->current_orientation, (CON_AXIS2), "along %s Z");
924                                         }
925                                         t->redraw |= TREDRAW_HARD;
926                                 }
927                                 break;
928                         case TFM_MODAL_PLANE_X:
929                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
930                                         if (cmode == 'X') {
931                                                 stopConstraint(t);
932                                         }
933                                         else {
934                                                 setUserConstraint(t, t->current_orientation, (CON_AXIS1 | CON_AXIS2), "locking %s X");
935                                         }
936                                         t->redraw |= TREDRAW_HARD;
937                                 }
938                                 break;
939                         case TFM_MODAL_PLANE_Y:
940                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
941                                         if (cmode == 'Y') {
942                                                 stopConstraint(t);
943                                         }
944                                         else {
945                                                 setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS2), "locking %s Y");
946                                         }
947                                         t->redraw |= TREDRAW_HARD;
948                                 }
949                                 break;
950                         case TFM_MODAL_PLANE_Z:
951                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
952                                         if (cmode == 'Z') {
953                                                 stopConstraint(t);
954                                         }
955                                         else {
956                                                 setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS1), "locking %s Z");
957                                         }
958                                         t->redraw |= TREDRAW_HARD;
959                                 }
960                                 break;
961                         case TFM_MODAL_CONS_OFF:
962                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
963                                         stopConstraint(t);
964                                         t->redraw |= TREDRAW_HARD;
965                                 }
966                                 break;
967                         case TFM_MODAL_ADD_SNAP:
968                                 addSnapPoint(t);
969                                 t->redraw |= TREDRAW_HARD;
970                                 break;
971                         case TFM_MODAL_REMOVE_SNAP:
972                                 removeSnapPoint(t);
973                                 t->redraw |= TREDRAW_HARD;
974                                 break;
975                         case TFM_MODAL_PROPSIZE_UP:
976                                 if (t->flag & T_PROP_EDIT) {
977                                         t->prop_size *= 1.1f;
978                                         if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
979                                                 t->prop_size = minf(t->prop_size, ((View3D *)t->view)->far);
980                                         calculatePropRatio(t);
981                                 }
982                                 t->redraw |= TREDRAW_HARD;
983                                 break;
984                         case TFM_MODAL_PROPSIZE_DOWN:
985                                 if (t->flag & T_PROP_EDIT) {
986                                         t->prop_size *= 0.90909090f;
987                                         calculatePropRatio(t);
988                                 }
989                                 t->redraw |= TREDRAW_HARD;
990                                 break;
991                         case TFM_MODAL_EDGESLIDE_UP:
992                         case TFM_MODAL_EDGESLIDE_DOWN:
993                                 t->redraw |= TREDRAW_HARD;
994                                 break;
995                         case TFM_MODAL_AUTOIK_LEN_INC:
996                                 if (t->flag & T_AUTOIK)
997                                         transform_autoik_update(t, 1);
998                                 t->redraw |= TREDRAW_HARD;
999                                 break;
1000                         case TFM_MODAL_AUTOIK_LEN_DEC:
1001                                 if (t->flag & T_AUTOIK) 
1002                                         transform_autoik_update(t, -1);
1003                                 t->redraw |= TREDRAW_HARD;
1004                                 break;
1005                         default:
1006                                 handled = 0;
1007                                 break;
1008                 }
1009
1010                 // Modal numinput events
1011                 t->redraw |= handleNumInput(&(t->num), event);
1012         }
1013         /* else do non-mapped events */
1014         else if (event->val == KM_PRESS) {
1015                 switch (event->type) {
1016                         case RIGHTMOUSE:
1017                                 t->state = TRANS_CANCEL;
1018                                 break;
1019                         /* enforce redraw of transform when modifiers are used */
1020                         case LEFTSHIFTKEY:
1021                         case RIGHTSHIFTKEY:
1022                                 t->modifiers |= MOD_CONSTRAINT_PLANE;
1023                                 t->redraw |= TREDRAW_HARD;
1024                                 break;
1025
1026                         case SPACEKEY:
1027                                 if ((t->spacetype == SPACE_VIEW3D) && event->alt) {
1028 #if 0 // TRANSFORM_FIX_ME
1029                                         int mval[2];
1030
1031                                         getmouseco_sc(mval);
1032                                         BIF_selectOrientation();
1033                                         calc_manipulator_stats(curarea);
1034                                         copy_m3_m4(t->spacemtx, G.vd->twmat);
1035                                         warp_pointer(mval[0], mval[1]);
1036 #endif
1037                                 }
1038                                 else {
1039                                         t->state = TRANS_CONFIRM;
1040                                 }
1041                                 break;
1042
1043                         case MIDDLEMOUSE:
1044                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1045                                         /* exception for switching to dolly, or trackball, in camera view */
1046                                         if (t->flag & T_CAMERA) {
1047                                                 if (t->mode == TFM_TRANSLATION)
1048                                                         setLocalConstraint(t, (CON_AXIS2), "along local Z");
1049                                                 else if (t->mode == TFM_ROTATION) {
1050                                                         restoreTransObjects(t);
1051                                                         initTrackball(t);
1052                                                 }
1053                                         }
1054                                         else {
1055                                                 t->modifiers |= MOD_CONSTRAINT_SELECT;
1056                                                 if (t->con.mode & CON_APPLY) {
1057                                                         stopConstraint(t);
1058                                                 }
1059                                                 else {
1060                                                         if (event->shift) {
1061                                                                 initSelectConstraint(t, t->spacemtx);
1062                                                         }
1063                                                         else {
1064                                                                 /* bit hackish... but it prevents mmb select to print the orientation from menu */
1065                                                                 strcpy(t->spacename, "global");
1066                                                                 initSelectConstraint(t, mati);
1067                                                         }
1068                                                         postSelectConstraint(t);
1069                                                 }
1070                                         }
1071                                         t->redraw |= TREDRAW_HARD;
1072                                 }
1073                                 break;
1074                         case ESCKEY:
1075                                 t->state = TRANS_CANCEL;
1076                                 break;
1077                         case PADENTER:
1078                         case RETKEY:
1079                                 t->state = TRANS_CONFIRM;
1080                                 break;
1081                         case GKEY:
1082                                 /* only switch when... */
1083                                 if (ELEM3(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL) ) {
1084                                         resetTransRestrictions(t);
1085                                         restoreTransObjects(t);
1086                                         initTranslation(t);
1087                                         initSnapping(t, NULL); // need to reinit after mode change
1088                                         t->redraw |= TREDRAW_HARD;
1089                                 }
1090                                 break;
1091                         case SKEY:
1092                                 /* only switch when... */
1093                                 if (ELEM3(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL) ) {
1094                                         resetTransRestrictions(t);
1095                                         restoreTransObjects(t);
1096                                         initResize(t);
1097                                         initSnapping(t, NULL); // need to reinit after mode change
1098                                         t->redraw |= TREDRAW_HARD;
1099                                 }
1100                                 break;
1101                         case RKEY:
1102                                 /* only switch when... */
1103                                 if (!(t->options & CTX_TEXTURE)) {
1104                                         if (ELEM4(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION) ) {
1105
1106                                                 resetTransRestrictions(t);
1107
1108                                                 if (t->mode == TFM_ROTATION) {
1109                                                         restoreTransObjects(t);
1110                                                         initTrackball(t);
1111                                                 }
1112                                                 else {
1113                                                         restoreTransObjects(t);
1114                                                         initRotation(t);
1115                                                 }
1116                                                 initSnapping(t, NULL); // need to reinit after mode change
1117                                                 t->redraw |= TREDRAW_HARD;
1118                                         }
1119                                 }
1120                                 break;
1121                         case CKEY:
1122                                 if (event->alt) {
1123                                         t->flag ^= T_PROP_CONNECTED;
1124                                         sort_trans_data_dist(t);
1125                                         calculatePropRatio(t);
1126                                         t->redraw = 1;
1127                                 }
1128                                 else {
1129                                         stopConstraint(t);
1130                                         t->redraw |= TREDRAW_HARD;
1131                                 }
1132                                 break;
1133                         case XKEY:
1134                         case YKEY:
1135                         case ZKEY:
1136                                 transform_event_xyz_constraint(t, event->type, cmode);
1137                                 break;
1138                         case OKEY:
1139                                 if (t->flag & T_PROP_EDIT && event->shift) {
1140                                         t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1141                                         calculatePropRatio(t);
1142                                         t->redraw |= TREDRAW_HARD;
1143                                 }
1144                                 break;
1145                         case PADPLUSKEY:
1146                                 if (event->alt && t->flag & T_PROP_EDIT) {
1147                                         t->prop_size *= 1.1f;
1148                                         if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1149                                                 t->prop_size = minf(t->prop_size, ((View3D *)t->view)->far);
1150                                         calculatePropRatio(t);
1151                                 }
1152                                 t->redraw = 1;
1153                                 break;
1154                         case PAGEUPKEY:
1155                         case WHEELDOWNMOUSE:
1156                                 if (t->flag & T_AUTOIK) {
1157                                         transform_autoik_update(t, 1);
1158                                 }
1159                                 else view_editmove(event->type);
1160                                 t->redraw = 1;
1161                                 break;
1162                         case PADMINUS:
1163                                 if (event->alt && t->flag & T_PROP_EDIT) {
1164                                         t->prop_size *= 0.90909090f;
1165                                         calculatePropRatio(t);
1166                                 }
1167                                 t->redraw = 1;
1168                                 break;
1169                         case PAGEDOWNKEY:
1170                         case WHEELUPMOUSE:
1171                                 if (t->flag & T_AUTOIK) {
1172                                         transform_autoik_update(t, -1);
1173                                 }
1174                                 else view_editmove(event->type);
1175                                 t->redraw = 1;
1176                                 break;
1177                         default:
1178                                 handled = 0;
1179                                 break;
1180                 }
1181
1182                 // Numerical input events
1183                 t->redraw |= handleNumInput(&(t->num), event);
1184
1185                 // Snapping key events
1186                 t->redraw |= handleSnapping(t, event);
1187
1188         }
1189         else if (event->val == KM_RELEASE) {
1190                 switch (event->type) {
1191                         case LEFTSHIFTKEY:
1192                         case RIGHTSHIFTKEY:
1193                                 t->modifiers &= ~MOD_CONSTRAINT_PLANE;
1194                                 t->redraw |= TREDRAW_HARD;
1195                                 break;
1196
1197                         case MIDDLEMOUSE:
1198                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1199                                         t->modifiers &= ~MOD_CONSTRAINT_SELECT;
1200                                         postSelectConstraint(t);
1201                                         t->redraw |= TREDRAW_HARD;
1202                                 }
1203                                 break;
1204 //              case LEFTMOUSE:
1205 //              case RIGHTMOUSE:
1206 //                      if (WM_modal_tweak_exit(event, t->event_type))
1207 ////                    if (t->options & CTX_TWEAK)
1208 //                              t->state = TRANS_CONFIRM;
1209 //                      break;
1210                         default:
1211                                 handled = 0;
1212                                 break;
1213                 }
1214
1215                 /* confirm transform if launch key is released after mouse move */
1216                 if (t->flag & T_RELEASE_CONFIRM) {
1217                         /* XXX Keyrepeat bug in Xorg fucks this up, will test when fixed */
1218                         if (event->type == t->launch_event && (t->launch_event == LEFTMOUSE || t->launch_event == RIGHTMOUSE)) {
1219                                 t->state = TRANS_CONFIRM;
1220                         }
1221                 }
1222         }
1223
1224         // Per transform event, if present
1225         if (t->handleEvent)
1226                 t->redraw |= t->handleEvent(t, event);
1227
1228         if (handled || t->redraw)
1229                 return 0;
1230         else
1231                 return OPERATOR_PASS_THROUGH;
1232 }
1233
1234 int calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], int cent2d[2])
1235 {
1236         TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
1237         int success;
1238
1239         t->state = TRANS_RUNNING;
1240
1241         t->options = CTX_NONE;
1242
1243         t->mode = TFM_DUMMY;
1244
1245         initTransInfo(C, t, NULL, NULL);    // internal data, mouse, vectors
1246
1247         createTransData(C, t);              // make TransData structs from selection
1248
1249         t->around = centerMode;             // override userdefined mode
1250
1251         if (t->total == 0) {
1252                 success = FALSE;
1253         }
1254         else {
1255                 success = TRUE;
1256
1257                 calculateCenter(t);
1258
1259                 if (cent2d) {
1260                         copy_v2_v2_int(cent2d, t->center2d);
1261                 }
1262
1263                 if (cent3d) {
1264                         // Copy center from constraint center. Transform center can be local
1265                         copy_v3_v3(cent3d, t->con.center);
1266                 }
1267         }
1268
1269
1270         /* aftertrans does insert ipos and action channels, and clears base flags, doesnt read transdata */
1271         special_aftertrans_update(C, t);
1272
1273         postTrans(C, t);
1274
1275         MEM_freeN(t);
1276
1277         return success;
1278 }
1279
1280 typedef enum {
1281         UP,
1282         DOWN,
1283         LEFT,
1284         RIGHT
1285 } ArrowDirection;
1286 static void drawArrow(ArrowDirection d, short offset, short length, short size)
1287 {
1288         switch (d) {
1289                 case LEFT:
1290                         offset = -offset;
1291                         length = -length;
1292                         size = -size;
1293                 case RIGHT:
1294                         glBegin(GL_LINES);
1295                         glVertex2s(offset, 0);
1296                         glVertex2s(offset + length, 0);
1297                         glVertex2s(offset + length, 0);
1298                         glVertex2s(offset + length - size, -size);
1299                         glVertex2s(offset + length, 0);
1300                         glVertex2s(offset + length - size,  size);
1301                         glEnd();
1302                         break;
1303                 case DOWN:
1304                         offset = -offset;
1305                         length = -length;
1306                         size = -size;
1307                 case UP:
1308                         glBegin(GL_LINES);
1309                         glVertex2s(0, offset);
1310                         glVertex2s(0, offset + length);
1311                         glVertex2s(0, offset + length);
1312                         glVertex2s(-size, offset + length - size);
1313                         glVertex2s(0, offset + length);
1314                         glVertex2s(size, offset + length - size);
1315                         glEnd();
1316                         break;
1317         }
1318 }
1319
1320 static void drawArrowHead(ArrowDirection d, short size)
1321 {
1322         switch (d) {
1323                 case LEFT:
1324                         size = -size;
1325                 case RIGHT:
1326                         glBegin(GL_LINES);
1327                         glVertex2s(0, 0);
1328                         glVertex2s(-size, -size);
1329                         glVertex2s(0, 0);
1330                         glVertex2s(-size,  size);
1331                         glEnd();
1332                         break;
1333                 case DOWN:
1334                         size = -size;
1335                 case UP:
1336                         glBegin(GL_LINES);
1337                         glVertex2s(0, 0);
1338                         glVertex2s(-size, -size);
1339                         glVertex2s(0, 0);
1340                         glVertex2s(size, -size);
1341                         glEnd();
1342                         break;
1343         }
1344 }
1345
1346 static void drawArc(float size, float angle_start, float angle_end, int segments)
1347 {
1348         float delta = (angle_end - angle_start) / segments;
1349         float angle;
1350         int a;
1351
1352         glBegin(GL_LINE_STRIP);
1353
1354         for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
1355                 glVertex2f(cosf(angle) * size, sinf(angle) * size);
1356         }
1357         glVertex2f(cosf(angle_end) * size, sinf(angle_end) * size);
1358
1359         glEnd();
1360 }
1361
1362 static int helpline_poll(bContext *C)
1363 {
1364         ARegion *ar = CTX_wm_region(C);
1365         
1366         if (ar && ar->regiontype == RGN_TYPE_WINDOW)
1367                 return 1;
1368         return 0;
1369 }
1370
1371 static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
1372 {
1373         TransInfo *t = (TransInfo *)customdata;
1374
1375         if (t->helpline != HLP_NONE && !(t->flag & T_USES_MANIPULATOR)) {
1376                 float vecrot[3], cent[2];
1377                 int mval[2];
1378
1379                 mval[0] = x;
1380                 mval[1] = y;
1381
1382                 copy_v3_v3(vecrot, t->center);
1383                 if (t->flag & T_EDIT) {
1384                         Object *ob = t->obedit;
1385                         if (ob) mul_m4_v3(ob->obmat, vecrot);
1386                 }
1387                 else if (t->flag & T_POSE) {
1388                         Object *ob = t->poseobj;
1389                         if (ob) mul_m4_v3(ob->obmat, vecrot);
1390                 }
1391
1392                 projectFloatView(t, vecrot, cent);  // no overflow in extreme cases
1393
1394                 glPushMatrix();
1395
1396                 switch (t->helpline) {
1397                         case HLP_SPRING:
1398                                 UI_ThemeColor(TH_WIRE);
1399
1400                                 setlinestyle(3);
1401                                 glBegin(GL_LINE_STRIP);
1402                                 glVertex2iv(t->mval);
1403                                 glVertex2fv(cent);
1404                                 glEnd();
1405
1406                                 glTranslatef(mval[0], mval[1], 0);
1407                                 glRotatef(-RAD2DEGF(atan2f(cent[0] - t->mval[0], cent[1] - t->mval[1])), 0, 0, 1);
1408
1409                                 setlinestyle(0);
1410                                 glLineWidth(3.0);
1411                                 drawArrow(UP, 5, 10, 5);
1412                                 drawArrow(DOWN, 5, 10, 5);
1413                                 glLineWidth(1.0);
1414                                 break;
1415                         case HLP_HARROW:
1416                                 UI_ThemeColor(TH_WIRE);
1417
1418                                 glTranslatef(mval[0], mval[1], 0);
1419
1420                                 glLineWidth(3.0);
1421                                 drawArrow(RIGHT, 5, 10, 5);
1422                                 drawArrow(LEFT, 5, 10, 5);
1423                                 glLineWidth(1.0);
1424                                 break;
1425                         case HLP_VARROW:
1426                                 UI_ThemeColor(TH_WIRE);
1427
1428                                 glTranslatef(mval[0], mval[1], 0);
1429
1430                                 glLineWidth(3.0);
1431                                 glBegin(GL_LINES);
1432                                 drawArrow(UP, 5, 10, 5);
1433                                 drawArrow(DOWN, 5, 10, 5);
1434                                 glLineWidth(1.0);
1435                                 break;
1436                         case HLP_ANGLE:
1437                         {
1438                                 float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1];
1439                                 float angle = atan2f(dy, dx);
1440                                 float dist = sqrtf(dx * dx + dy * dy);
1441                                 float delta_angle = minf(15.0f / dist, (float)M_PI / 4.0f);
1442                                 float spacing_angle = minf(5.0f / dist, (float)M_PI / 12.0f);
1443                                 UI_ThemeColor(TH_WIRE);
1444
1445                                 setlinestyle(3);
1446                                 glBegin(GL_LINE_STRIP);
1447                                 glVertex2iv(t->mval);
1448                                 glVertex2fv(cent);
1449                                 glEnd();
1450
1451                                 glTranslatef(cent[0] - t->mval[0] + mval[0], cent[1] - t->mval[1] + mval[1], 0);
1452
1453                                 setlinestyle(0);
1454                                 glLineWidth(3.0);
1455                                 drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
1456                                 drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
1457
1458                                 glPushMatrix();
1459
1460                                 glTranslatef(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
1461                                 glRotatef(RAD2DEGF(angle - delta_angle), 0, 0, 1);
1462
1463                                 drawArrowHead(DOWN, 5);
1464
1465                                 glPopMatrix();
1466
1467                                 glTranslatef(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
1468                                 glRotatef(RAD2DEGF(angle + delta_angle), 0, 0, 1);
1469
1470                                 drawArrowHead(UP, 5);
1471
1472                                 glLineWidth(1.0);
1473                                 break;
1474                         }
1475                         case HLP_TRACKBALL:
1476                         {
1477                                 unsigned char col[3], col2[3];
1478                                 UI_GetThemeColor3ubv(TH_GRID, col);
1479
1480                                 glTranslatef(mval[0], mval[1], 0);
1481
1482                                 glLineWidth(3.0);
1483
1484                                 UI_make_axis_color(col, col2, 'X');
1485                                 glColor3ubv((GLubyte *)col2);
1486
1487                                 drawArrow(RIGHT, 5, 10, 5);
1488                                 drawArrow(LEFT, 5, 10, 5);
1489
1490                                 UI_make_axis_color(col, col2, 'Y');
1491                                 glColor3ubv((GLubyte *)col2);
1492
1493                                 drawArrow(UP, 5, 10, 5);
1494                                 drawArrow(DOWN, 5, 10, 5);
1495                                 glLineWidth(1.0);
1496                                 break;
1497                         }
1498                 }
1499
1500                 glPopMatrix();
1501         }
1502 }
1503
1504 static void drawTransformView(const struct bContext *C, ARegion *UNUSED(ar), void *arg)
1505 {
1506         TransInfo *t = arg;
1507
1508         drawConstraint(t);
1509         drawPropCircle(C, t);
1510         drawSnapping(C, t);
1511         drawNonPropEdge(C, t);
1512 }
1513
1514 #if 0
1515 static void drawTransformPixel(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *UNUSED(arg))
1516 {
1517 //      TransInfo *t = arg;
1518 //
1519 //      drawHelpline(C, t->mval[0], t->mval[1], t);
1520 }
1521 #endif
1522
1523 void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
1524 {
1525         ToolSettings *ts = CTX_data_tool_settings(C);
1526         int constraint_axis[3] = {0, 0, 0};
1527         int proportional = 0;
1528         PropertyRNA *prop;
1529
1530         // Save back mode in case we're in the generic operator
1531         if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
1532                 RNA_property_enum_set(op->ptr, prop, t->mode);
1533         }
1534
1535         if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
1536                 float *values = (t->flag & T_AUTOVALUES) ? t->auto_values : t->values;
1537                 if (RNA_property_array_check(prop)) {
1538                         RNA_property_float_set_array(op->ptr, prop, values);
1539                 }
1540                 else {
1541                         RNA_property_float_set(op->ptr, prop, values[0]);
1542                 }
1543         }
1544
1545         /* convert flag to enum */
1546         switch (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
1547                 case (T_PROP_EDIT | T_PROP_CONNECTED):
1548                         proportional = PROP_EDIT_CONNECTED;
1549                         break;
1550                 case T_PROP_EDIT:
1551                         proportional = PROP_EDIT_ON;
1552                         break;
1553                 default:
1554                         proportional = PROP_EDIT_OFF;
1555         }
1556
1557         // If modal, save settings back in scene if not set as operator argument
1558         if (t->flag & T_MODAL) {
1559
1560                 /* save settings if not set in operator */
1561                 if ((prop = RNA_struct_find_property(op->ptr, "proportional")) &&
1562                     !RNA_property_is_set(op->ptr, prop))
1563                 {
1564                         if (t->obedit)
1565                                 ts->proportional = proportional;
1566                         else if (t->options & CTX_MASK)
1567                                 ts->proportional_mask = (proportional != PROP_EDIT_OFF);
1568                         else
1569                                 ts->proportional_objects = (proportional != PROP_EDIT_OFF);
1570                 }
1571
1572                 if ((prop = RNA_struct_find_property(op->ptr, "proportional_size")) &&
1573                     !RNA_property_is_set(op->ptr, prop))
1574                 {
1575                         ts->proportional_size = t->prop_size;
1576                 }
1577
1578                 if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
1579                     !RNA_property_is_set(op->ptr, prop))
1580                 {
1581                         ts->prop_mode = t->prop_mode;
1582                 }
1583                 
1584                 /* do we check for parameter? */
1585                 if (t->modifiers & MOD_SNAP) {
1586                         ts->snap_flag |= SCE_SNAP;
1587                 }
1588                 else {
1589                         ts->snap_flag &= ~SCE_SNAP;
1590                 }
1591
1592                 if (t->spacetype == SPACE_VIEW3D) {
1593                         if ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) &&
1594                             !RNA_property_is_set(op->ptr, prop))
1595                         {
1596                                 View3D *v3d = t->view;
1597
1598                                 v3d->twmode = t->current_orientation;
1599                         }
1600                 }
1601         }
1602         
1603         if (RNA_struct_find_property(op->ptr, "proportional")) {
1604                 RNA_enum_set(op->ptr, "proportional", proportional);
1605                 RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
1606                 RNA_float_set(op->ptr, "proportional_size", t->prop_size);
1607         }
1608
1609         if ((prop = RNA_struct_find_property(op->ptr, "axis"))) {
1610                 RNA_property_float_set_array(op->ptr, prop, t->axis);
1611         }
1612
1613         if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
1614                 RNA_property_boolean_set(op->ptr, prop, t->flag & T_MIRROR);
1615         }
1616
1617         if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
1618                 /* constraint orientation can be global, event if user selects something else
1619                  * so use the orientation in the constraint if set
1620                  * */
1621                 if (t->con.mode & CON_APPLY) {
1622                         RNA_enum_set(op->ptr, "constraint_orientation", t->con.orientation);
1623                 }
1624                 else {
1625                         RNA_enum_set(op->ptr, "constraint_orientation", t->current_orientation);
1626                 }
1627
1628                 if (t->con.mode & CON_APPLY) {
1629                         if (t->con.mode & CON_AXIS0) {
1630                                 constraint_axis[0] = 1;
1631                         }
1632                         if (t->con.mode & CON_AXIS1) {
1633                                 constraint_axis[1] = 1;
1634                         }
1635                         if (t->con.mode & CON_AXIS2) {
1636                                 constraint_axis[2] = 1;
1637                         }
1638                 }
1639
1640                 RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
1641         }
1642 }
1643
1644 /* note: caller needs to free 't' on a 0 return */
1645 int initTransform(bContext *C, TransInfo *t, wmOperator *op, wmEvent *event, int mode)
1646 {
1647         int options = 0;
1648         PropertyRNA *prop;
1649
1650         t->context = C;
1651
1652         /* added initialize, for external calls to set stuff in TransInfo, like undo string */
1653
1654         t->state = TRANS_STARTING;
1655
1656         if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) && RNA_property_is_set(op->ptr, prop)) {
1657                 if (RNA_property_boolean_get(op->ptr, prop)) {
1658                         options |= CTX_TEXTURE;
1659                 }
1660         }
1661         
1662         t->options = options;
1663
1664         t->mode = mode;
1665
1666         t->launch_event = event ? event->type : -1;
1667
1668         if (t->launch_event == EVT_TWEAK_R) {
1669                 t->launch_event = RIGHTMOUSE;
1670         }
1671         else if (t->launch_event == EVT_TWEAK_L) {
1672                 t->launch_event = LEFTMOUSE;
1673         }
1674
1675         // XXX Remove this when wm_operator_call_internal doesn't use window->eventstate (which can have type = 0)
1676         // For manipulator only, so assume LEFTMOUSE
1677         if (t->launch_event == 0) {
1678                 t->launch_event = LEFTMOUSE;
1679         }
1680
1681         if (!initTransInfo(C, t, op, event)) {  /* internal data, mouse, vectors */
1682                 return 0;
1683         }
1684
1685         if (t->spacetype == SPACE_VIEW3D) {
1686                 //calc_manipulator_stats(curarea);
1687                 initTransformOrientation(C, t);
1688
1689                 t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
1690                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
1691                 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
1692                 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
1693         }
1694         else if (t->spacetype == SPACE_IMAGE) {
1695                 unit_m3(t->spacemtx);
1696                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
1697                 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
1698                 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
1699         }
1700         else if (t->spacetype == SPACE_CLIP) {
1701                 unit_m3(t->spacemtx);
1702                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
1703         }
1704         else if (t->spacetype == SPACE_NODE) {
1705                 unit_m3(t->spacemtx);
1706                 /*t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);*/
1707                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
1708                 /*t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);*/
1709         }
1710         else
1711                 unit_m3(t->spacemtx);
1712
1713         createTransData(C, t);          // make TransData structs from selection
1714
1715         if (t->total == 0) {
1716                 postTrans(C, t);
1717                 return 0;
1718         }
1719
1720         /* Stupid code to have Ctrl-Click on manipulator work ok */
1721         if (event) {
1722                 /* do this only for translation/rotation/resize due to only this
1723                  * moded are available from manipulator and doing such check could
1724                  * lead to keymap conflicts for other modes (see #31584)
1725                  */
1726                 if (ELEM3(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
1727                         wmKeyMap *keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1728                         wmKeyMapItem *kmi;
1729
1730                         for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
1731                                 if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
1732                                         if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) &&   event->ctrl)  ||
1733                                             (ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
1734                                             (ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) &&     event->alt)   ||
1735                                             ((kmi->type == OSKEY) &&                         event->oskey) )
1736                                         {
1737                                                 t->modifiers |= MOD_SNAP_INVERT;
1738                                         }
1739                                         break;
1740                                 }
1741                         }
1742                 }
1743
1744         }
1745
1746         initSnapping(t, op); // Initialize snapping data AFTER mode flags
1747
1748         /* EVIL! posemode code can switch translation to rotate when 1 bone is selected. will be removed (ton) */
1749         /* EVIL2: we gave as argument also texture space context bit... was cleared */
1750         /* EVIL3: extend mode for animation editors also switches modes... but is best way to avoid duplicate code */
1751         mode = t->mode;
1752
1753         calculatePropRatio(t);
1754         calculateCenter(t);
1755
1756         initMouseInput(t, &t->mouse, t->center2d, t->imval);
1757
1758         switch (mode) {
1759                 case TFM_TRANSLATION:
1760                         initTranslation(t);
1761                         break;
1762                 case TFM_ROTATION:
1763                         initRotation(t);
1764                         break;
1765                 case TFM_RESIZE:
1766                         initResize(t);
1767                         break;
1768                 case TFM_SKIN_RESIZE:
1769                         initSkinResize(t);
1770                         break;
1771                 case TFM_TOSPHERE:
1772                         initToSphere(t);
1773                         break;
1774                 case TFM_SHEAR:
1775                         initShear(t);
1776                         break;
1777                 case TFM_WARP:
1778                         initWarp(t);
1779                         break;
1780                 case TFM_SHRINKFATTEN:
1781                         initShrinkFatten(t);
1782                         break;
1783                 case TFM_TILT:
1784                         initTilt(t);
1785                         break;
1786                 case TFM_CURVE_SHRINKFATTEN:
1787                         initCurveShrinkFatten(t);
1788                         break;
1789                 case TFM_MASK_SHRINKFATTEN:
1790                         initMaskShrinkFatten(t);
1791                         break;
1792                 case TFM_TRACKBALL:
1793                         initTrackball(t);
1794                         break;
1795                 case TFM_PUSHPULL:
1796                         initPushPull(t);
1797                         break;
1798                 case TFM_CREASE:
1799                         initCrease(t);
1800                         break;
1801                 case TFM_BONESIZE:
1802                 {   /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
1803                         bArmature *arm = t->poseobj->data;
1804                         if (arm->drawtype == ARM_ENVELOPE)
1805                                 initBoneEnvelope(t);
1806                         else
1807                                 initBoneSize(t);
1808                 }
1809                 break;
1810                 case TFM_BONE_ENVELOPE:
1811                         initBoneEnvelope(t);
1812                         break;
1813                 case TFM_EDGE_SLIDE:
1814                         initEdgeSlide(t);
1815                         break;
1816                 case TFM_BONE_ROLL:
1817                         initBoneRoll(t);
1818                         break;
1819                 case TFM_TIME_TRANSLATE:
1820                         initTimeTranslate(t);
1821                         break;
1822                 case TFM_TIME_SLIDE:
1823                         initTimeSlide(t);
1824                         break;
1825                 case TFM_TIME_SCALE:
1826                         initTimeScale(t);
1827                         break;
1828                 case TFM_TIME_DUPLICATE:
1829                         /* same as TFM_TIME_EXTEND, but we need the mode info for later
1830                          * so that duplicate-culling will work properly
1831                          */
1832                         if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
1833                                 initTranslation(t);
1834                         else
1835                                 initTimeTranslate(t);
1836                         t->mode = mode;
1837                         break;
1838                 case TFM_TIME_EXTEND:
1839                         /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
1840                          * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
1841                          * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
1842                          * depending on which editor this was called from
1843                          */
1844                         if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
1845                                 initTranslation(t);
1846                         else
1847                                 initTimeTranslate(t);
1848                         break;
1849                 case TFM_BAKE_TIME:
1850                         initBakeTime(t);
1851                         break;
1852                 case TFM_MIRROR:
1853                         initMirror(t);
1854                         break;
1855                 case TFM_BEVEL:
1856                         initBevel(t);
1857                         break;
1858                 case TFM_BWEIGHT:
1859                         initBevelWeight(t);
1860                         break;
1861                 case TFM_ALIGN:
1862                         initAlign(t);
1863                         break;
1864                 case TFM_SEQ_SLIDE:
1865                         initSeqSlide(t);
1866                         break;
1867         }
1868
1869         if (t->state == TRANS_CANCEL) {
1870                 postTrans(C, t);
1871                 return 0;
1872         }
1873
1874
1875         /* overwrite initial values if operator supplied a non-null vector */
1876         if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
1877                 float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory  */
1878
1879                 if (RNA_property_array_check(prop)) {
1880                         RNA_float_get_array(op->ptr, "value", values);
1881                 }
1882                 else {
1883                         values[0] = RNA_float_get(op->ptr, "value");
1884                 }
1885
1886                 copy_v4_v4(t->values, values);
1887                 copy_v4_v4(t->auto_values, values);
1888                 t->flag |= T_AUTOVALUES;
1889         }
1890
1891         /* Transformation axis from operator */
1892         if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) {
1893                 RNA_property_float_get_array(op->ptr, prop, t->axis);
1894                 normalize_v3(t->axis);
1895                 copy_v3_v3(t->axis_orig, t->axis);
1896         }
1897
1898         /* Constraint init from operator */
1899         if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) {
1900                 int constraint_axis[3];
1901
1902                 RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
1903
1904                 if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
1905                         t->con.mode |= CON_APPLY;
1906
1907                         if (constraint_axis[0]) {
1908                                 t->con.mode |= CON_AXIS0;
1909                         }
1910                         if (constraint_axis[1]) {
1911                                 t->con.mode |= CON_AXIS1;
1912                         }
1913                         if (constraint_axis[2]) {
1914                                 t->con.mode |= CON_AXIS2;
1915                         }
1916
1917                         setUserConstraint(t, t->current_orientation, t->con.mode, "%s");
1918                 }
1919         }
1920
1921         t->context = NULL;
1922
1923         return 1;
1924 }
1925
1926 void transformApply(bContext *C, TransInfo *t)
1927 {
1928         t->context = C;
1929
1930         if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
1931                 selectConstraint(t);
1932                 if (t->transform) {
1933                         t->transform(t, t->mval);  // calls recalcData()
1934                         viewRedrawForce(C, t);
1935                 }
1936                 t->redraw = TREDRAW_NOTHING;
1937         }
1938         else if (t->redraw & TREDRAW_SOFT) {
1939                 viewRedrawForce(C, t);
1940         }
1941
1942         /* If auto confirm is on, break after one pass */
1943         if (t->options & CTX_AUTOCONFIRM) {
1944                 t->state = TRANS_CONFIRM;
1945         }
1946
1947         if (BKE_ptcache_get_continue_physics()) {
1948                 // TRANSFORM_FIX_ME
1949                 //do_screenhandlers(G.curscreen);
1950                 t->redraw |= TREDRAW_HARD;
1951         }
1952
1953         t->context = NULL;
1954 }
1955
1956 static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
1957 {
1958         TransInfo *t = arg;
1959
1960         if (t->redraw & TREDRAW_SOFT) {
1961                 t->redraw |= TREDRAW_HARD;
1962                 transformApply((bContext *)C, t);
1963         }
1964 }
1965
1966 int transformEnd(bContext *C, TransInfo *t)
1967 {
1968         int exit_code = OPERATOR_RUNNING_MODAL;
1969
1970         t->context = C;
1971
1972         if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
1973                 /* handle restoring objects */
1974                 if (t->state == TRANS_CANCEL) {
1975                         /* exception, edge slide transformed UVs too */
1976                         if (t->mode == TFM_EDGE_SLIDE)
1977                                 doEdgeSlide(t, 0.0f);
1978                         
1979                         exit_code = OPERATOR_CANCELLED;
1980                         restoreTransObjects(t); // calls recalcData()
1981                 }
1982                 else {
1983                         exit_code = OPERATOR_FINISHED;
1984                 }
1985
1986                 /* aftertrans does insert keyframes, and clears base flags, doesnt read transdata */
1987                 special_aftertrans_update(C, t);
1988
1989                 /* free data */
1990                 postTrans(C, t);
1991
1992                 /* send events out for redraws */
1993                 viewRedrawPost(C, t);
1994
1995                 /*  Undo as last, certainly after special_trans_update! */
1996
1997                 if (t->state == TRANS_CANCEL) {
1998 //                      if (t->undostr) ED_undo_push(C, t->undostr);
1999                 }
2000                 else {
2001 //                      if (t->undostr) ED_undo_push(C, t->undostr);
2002 //                      else ED_undo_push(C, transform_to_undostr(t));
2003                 }
2004                 t->undostr = NULL;
2005
2006                 viewRedrawForce(C, t);
2007         }
2008
2009         t->context = NULL;
2010
2011         return exit_code;
2012 }
2013
2014 /* ************************** TRANSFORM LOCKS **************************** */
2015
2016 static void protectedTransBits(short protectflag, float *vec)
2017 {
2018         if (protectflag & OB_LOCK_LOCX)
2019                 vec[0] = 0.0f;
2020         if (protectflag & OB_LOCK_LOCY)
2021                 vec[1] = 0.0f;
2022         if (protectflag & OB_LOCK_LOCZ)
2023                 vec[2] = 0.0f;
2024 }
2025
2026 static void protectedSizeBits(short protectflag, float *size)
2027 {
2028         if (protectflag & OB_LOCK_SCALEX)
2029                 size[0] = 1.0f;
2030         if (protectflag & OB_LOCK_SCALEY)
2031                 size[1] = 1.0f;
2032         if (protectflag & OB_LOCK_SCALEZ)
2033                 size[2] = 1.0f;
2034 }
2035
2036 static void protectedRotateBits(short protectflag, float *eul, float *oldeul)
2037 {
2038         if (protectflag & OB_LOCK_ROTX)
2039                 eul[0] = oldeul[0];
2040         if (protectflag & OB_LOCK_ROTY)
2041                 eul[1] = oldeul[1];
2042         if (protectflag & OB_LOCK_ROTZ)
2043                 eul[2] = oldeul[2];
2044 }
2045
2046
2047 /* this function only does the delta rotation */
2048 /* axis-angle is usually internally stored as quats... */
2049 static void protectedAxisAngleBits(short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle)
2050 {
2051         /* check that protection flags are set */
2052         if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2053                 return;
2054         
2055         if (protectflag & OB_LOCK_ROT4D) {
2056                 /* axis-angle getting limited as 4D entities that they are... */
2057                 if (protectflag & OB_LOCK_ROTW)
2058                         *angle = oldAngle;
2059                 if (protectflag & OB_LOCK_ROTX)
2060                         axis[0] = oldAxis[0];
2061                 if (protectflag & OB_LOCK_ROTY)
2062                         axis[1] = oldAxis[1];
2063                 if (protectflag & OB_LOCK_ROTZ)
2064                         axis[2] = oldAxis[2];
2065         }
2066         else {
2067                 /* axis-angle get limited with euler... */
2068                 float eul[3], oldeul[3];
2069                 
2070                 axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle);
2071                 axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle);
2072                 
2073                 if (protectflag & OB_LOCK_ROTX)
2074                         eul[0] = oldeul[0];
2075                 if (protectflag & OB_LOCK_ROTY)
2076                         eul[1] = oldeul[1];
2077                 if (protectflag & OB_LOCK_ROTZ)
2078                         eul[2] = oldeul[2];
2079                 
2080                 eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT);
2081                 
2082                 /* when converting to axis-angle, we need a special exception for the case when there is no axis */
2083                 if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) {
2084                         /* for now, rotate around y-axis then (so that it simply becomes the roll) */
2085                         axis[1] = 1.0f;
2086                 }
2087         }
2088 }
2089
2090 /* this function only does the delta rotation */
2091 static void protectedQuaternionBits(short protectflag, float *quat, float *oldquat)
2092 {
2093         /* check that protection flags are set */
2094         if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2095                 return;
2096         
2097         if (protectflag & OB_LOCK_ROT4D) {
2098                 /* quaternions getting limited as 4D entities that they are... */
2099                 if (protectflag & OB_LOCK_ROTW)
2100                         quat[0] = oldquat[0];
2101                 if (protectflag & OB_LOCK_ROTX)
2102                         quat[1] = oldquat[1];
2103                 if (protectflag & OB_LOCK_ROTY)
2104                         quat[2] = oldquat[2];
2105                 if (protectflag & OB_LOCK_ROTZ)
2106                         quat[3] = oldquat[3];
2107         }
2108         else {
2109                 /* quaternions get limited with euler... (compatibility mode) */
2110                 float eul[3], oldeul[3], nquat[4], noldquat[4];
2111                 float qlen;
2112
2113                 qlen = normalize_qt_qt(nquat, quat);
2114                 normalize_qt_qt(noldquat, oldquat);
2115
2116                 quat_to_eul(eul, nquat);
2117                 quat_to_eul(oldeul, noldquat);
2118
2119                 if (protectflag & OB_LOCK_ROTX)
2120                         eul[0] = oldeul[0];
2121                 if (protectflag & OB_LOCK_ROTY)
2122                         eul[1] = oldeul[1];
2123                 if (protectflag & OB_LOCK_ROTZ)
2124                         eul[2] = oldeul[2];
2125
2126                 eul_to_quat(quat, eul);
2127
2128                 /* restore original quat size */
2129                 mul_qt_fl(quat, qlen);
2130                 
2131                 /* quaternions flip w sign to accumulate rotations correctly */
2132                 if ((nquat[0] < 0.0f && quat[0] > 0.0f) ||
2133                     (nquat[0] > 0.0f && quat[0] < 0.0f))
2134                 {
2135                         mul_qt_fl(quat, -1.0f);
2136                 }
2137         }
2138 }
2139
2140 /* ******************* TRANSFORM LIMITS ********************** */
2141
2142 static void constraintTransLim(TransInfo *t, TransData *td)
2143 {
2144         if (td->con) {
2145                 bConstraintTypeInfo *ctiLoc = get_constraint_typeinfo(CONSTRAINT_TYPE_LOCLIMIT);
2146                 bConstraintTypeInfo *ctiDist = get_constraint_typeinfo(CONSTRAINT_TYPE_DISTLIMIT);
2147                 
2148                 bConstraintOb cob = {NULL};
2149                 bConstraint *con;
2150                 float ctime = (float)(t->scene->r.cfra);
2151                 
2152                 /* Make a temporary bConstraintOb for using these limit constraints
2153                  *  - they only care that cob->matrix is correctly set ;-)
2154                  *      - current space should be local
2155                  */
2156                 unit_m4(cob.matrix);
2157                 copy_v3_v3(cob.matrix[3], td->loc);
2158                 
2159                 /* Evaluate valid constraints */
2160                 for (con = td->con; con; con = con->next) {
2161                         bConstraintTypeInfo *cti = NULL;
2162                         ListBase targets = {NULL, NULL};
2163                         float tmat[4][4];
2164                         
2165                         /* only consider constraint if enabled */
2166                         if (con->flag & CONSTRAINT_DISABLE) continue;
2167                         if (con->enforce == 0.0f) continue;
2168                         
2169                         /* only use it if it's tagged for this purpose (and the right type) */
2170                         if (con->type == CONSTRAINT_TYPE_LOCLIMIT) {
2171                                 bLocLimitConstraint *data = con->data;
2172                                 
2173                                 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2174                                         continue;
2175                                 cti = ctiLoc;
2176                         }
2177                         else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) {
2178                                 bDistLimitConstraint *data = con->data;
2179                                 
2180                                 if ((data->flag & LIMITDIST_TRANSFORM) == 0)
2181                                         continue;
2182                                 cti = ctiDist;
2183                         }
2184                         
2185                         if (cti) {
2186                                 /* do space conversions */
2187                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2188                                         /* just multiply by td->mtx (this should be ok) */
2189                                         copy_m4_m4(tmat, cob.matrix);
2190                                         mul_m4_m3m4(cob.matrix, td->mtx, tmat);
2191                                 }
2192                                 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2193                                         /* skip... incompatable spacetype */
2194                                         continue;
2195                                 }
2196                                 
2197                                 /* get constraint targets if needed */
2198                                 get_constraint_targets_for_solving(con, &cob, &targets, ctime);
2199                                 
2200                                 /* do constraint */
2201                                 cti->evaluate_constraint(con, &cob, &targets);
2202                                 
2203                                 /* convert spaces again */
2204                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2205                                         /* just multiply by td->mtx (this should be ok) */
2206                                         copy_m4_m4(tmat, cob.matrix);
2207                                         mul_m4_m3m4(cob.matrix, td->smtx, tmat);
2208                                 }
2209                                 
2210                                 /* free targets list */
2211                                 BLI_freelistN(&targets);
2212                         }
2213                 }
2214                 
2215                 /* copy results from cob->matrix */
2216                 copy_v3_v3(td->loc, cob.matrix[3]);
2217         }
2218 }
2219
2220 static void constraintob_from_transdata(bConstraintOb *cob, TransData *td)
2221 {
2222         /* Make a temporary bConstraintOb for use by limit constraints
2223          *  - they only care that cob->matrix is correctly set ;-)
2224          *      - current space should be local
2225          */
2226         memset(cob, 0, sizeof(bConstraintOb));
2227         if (td->ext) {
2228                 if (td->ext->rotOrder == ROT_MODE_QUAT) {
2229                         /* quats */
2230                         /* objects and bones do normalization first too, otherwise
2231                          * we don't necessarily end up with a rotation matrix, and
2232                          * then conversion back to quat gives a different result */
2233                         float quat[4];
2234                         normalize_qt_qt(quat, td->ext->quat);
2235                         quat_to_mat4(cob->matrix, quat);
2236                 }
2237                 else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2238                         /* axis angle */
2239                         axis_angle_to_mat4(cob->matrix, &td->ext->quat[1], td->ext->quat[0]);
2240                 }
2241                 else {
2242                         /* eulers */
2243                         eulO_to_mat4(cob->matrix, td->ext->rot, td->ext->rotOrder);
2244                 }
2245         }
2246 }
2247
2248 static void constraintRotLim(TransInfo *UNUSED(t), TransData *td)
2249 {
2250         if (td->con) {
2251                 bConstraintTypeInfo *cti = get_constraint_typeinfo(CONSTRAINT_TYPE_ROTLIMIT);
2252                 bConstraintOb cob;
2253                 bConstraint *con;
2254                 int do_limit = FALSE;
2255                 
2256                 /* Evaluate valid constraints */
2257                 for (con = td->con; con; con = con->next) {
2258                         /* only consider constraint if enabled */
2259                         if (con->flag & CONSTRAINT_DISABLE) continue;
2260                         if (con->enforce == 0.0f) continue;
2261                         
2262                         /* we're only interested in Limit-Rotation constraints */
2263                         if (con->type == CONSTRAINT_TYPE_ROTLIMIT) {
2264                                 bRotLimitConstraint *data = con->data;
2265                                 float tmat[4][4];
2266                                 
2267                                 /* only use it if it's tagged for this purpose */
2268                                 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2269                                         continue;
2270
2271                                 /* skip incompatable spacetypes */
2272                                 if (!ELEM(con->ownspace, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL))
2273                                         continue;
2274
2275                                 /* only do conversion if necessary, to preserve quats and eulers */
2276                                 if (do_limit == FALSE) {
2277                                         constraintob_from_transdata(&cob, td);
2278                                         do_limit = TRUE;
2279                                 }
2280                                 
2281                                 /* do space conversions */
2282                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2283                                         /* just multiply by td->mtx (this should be ok) */
2284                                         copy_m4_m4(tmat, cob.matrix);
2285                                         mul_m4_m3m4(cob.matrix, td->mtx, tmat);
2286                                 }
2287                                 
2288                                 /* do constraint */
2289                                 cti->evaluate_constraint(con, &cob, NULL);
2290                                 
2291                                 /* convert spaces again */
2292                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2293                                         /* just multiply by td->mtx (this should be ok) */
2294                                         copy_m4_m4(tmat, cob.matrix);
2295                                         mul_m4_m3m4(cob.matrix, td->smtx, tmat);
2296                                 }
2297                         }
2298                 }
2299                 
2300                 if (do_limit) {
2301                         /* copy results from cob->matrix */
2302                         if (td->ext->rotOrder == ROT_MODE_QUAT) {
2303                                 /* quats */
2304                                 mat4_to_quat(td->ext->quat, cob.matrix);
2305                         }
2306                         else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2307                                 /* axis angle */
2308                                 mat4_to_axis_angle(&td->ext->quat[1], &td->ext->quat[0], cob.matrix);
2309                         }
2310                         else {
2311                                 /* eulers */
2312                                 mat4_to_eulO(td->ext->rot, td->ext->rotOrder, cob.matrix);
2313                         }
2314                 }
2315         }
2316 }
2317
2318 static void constraintSizeLim(TransInfo *t, TransData *td)
2319 {
2320         if (td->con && td->ext) {
2321                 bConstraintTypeInfo *cti = get_constraint_typeinfo(CONSTRAINT_TYPE_SIZELIMIT);
2322                 bConstraintOb cob = {NULL};
2323                 bConstraint *con;
2324                 
2325                 /* Make a temporary bConstraintOb for using these limit constraints
2326                  *  - they only care that cob->matrix is correctly set ;-)
2327                  *      - current space should be local
2328                  */
2329                 if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2330                         /* scale val and reset size */
2331                         return; // TODO: fix this case
2332                 }
2333                 else {
2334                         /* Reset val if SINGLESIZE but using a constraint */
2335                         if (td->flag & TD_SINGLESIZE)
2336                                 return;
2337                         
2338                         size_to_mat4(cob.matrix, td->ext->size);
2339                 }
2340                 
2341                 /* Evaluate valid constraints */
2342                 for (con = td->con; con; con = con->next) {
2343                         /* only consider constraint if enabled */
2344                         if (con->flag & CONSTRAINT_DISABLE) continue;
2345                         if (con->enforce == 0.0f) continue;
2346                         
2347                         /* we're only interested in Limit-Scale constraints */
2348                         if (con->type == CONSTRAINT_TYPE_SIZELIMIT) {
2349                                 bSizeLimitConstraint *data = con->data;
2350                                 float tmat[4][4];
2351                                 
2352                                 /* only use it if it's tagged for this purpose */
2353                                 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2354                                         continue;
2355                                 
2356                                 /* do space conversions */
2357                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2358                                         /* just multiply by td->mtx (this should be ok) */
2359                                         copy_m4_m4(tmat, cob.matrix);
2360                                         mul_m4_m3m4(cob.matrix, td->mtx, tmat);
2361                                 }
2362                                 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2363                                         /* skip... incompatable spacetype */
2364                                         continue;
2365                                 }
2366                                 
2367                                 /* do constraint */
2368                                 cti->evaluate_constraint(con, &cob, NULL);
2369                                 
2370                                 /* convert spaces again */
2371                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2372                                         /* just multiply by td->mtx (this should be ok) */
2373                                         copy_m4_m4(tmat, cob.matrix);
2374                                         mul_m4_m3m4(cob.matrix, td->smtx, tmat);
2375                                 }
2376                         }
2377                 }
2378                 
2379                 /* copy results from cob->matrix */
2380                 if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2381                         /* scale val and reset size */
2382                         return; // TODO: fix this case
2383                 }
2384                 else {
2385                         /* Reset val if SINGLESIZE but using a constraint */
2386                         if (td->flag & TD_SINGLESIZE)
2387                                 return;
2388                         
2389                         mat4_to_size(td->ext->size, cob.matrix);
2390                 }
2391         }
2392 }
2393
2394 /* ************************** WARP *************************** */
2395
2396 static void postInputWarp(TransInfo *t, float values[3])
2397 {
2398         mul_v3_fl(values, (float)(M_PI * 2));
2399
2400         if (t->customData) { /* non-null value indicates reversed input */
2401                 negate_v3(values);
2402         }
2403 }
2404
2405 void initWarp(TransInfo *t)
2406 {
2407         float max[3], min[3];
2408         int i;
2409         
2410         t->mode = TFM_WARP;
2411         t->transform = Warp;
2412         t->handleEvent = handleEventWarp;
2413         
2414         setInputPostFct(&t->mouse, postInputWarp);
2415         initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO);
2416         
2417         t->idx_max = 0;
2418         t->num.idx_max = 0;
2419         t->snap[0] = 0.0f;
2420         t->snap[1] = 5.0f / 180.0f * (float)M_PI;
2421         t->snap[2] = 1.0f / 180.0f * (float)M_PI;
2422         
2423         t->num.increment = 1.0f;
2424
2425         t->flag |= T_NO_CONSTRAINT;
2426         
2427         /* we need min/max in view space */
2428         for (i = 0; i < t->total; i++) {
2429                 float center[3];
2430                 copy_v3_v3(center, t->data[i].center);
2431                 mul_m3_v3(t->data[i].mtx, center);
2432                 mul_m4_v3(t->viewmat, center);
2433                 sub_v3_v3(center, t->viewmat[3]);
2434                 if (i) {
2435                         minmax_v3v3_v3(min, max, center);
2436                 }
2437                 else {
2438                         copy_v3_v3(max, center);
2439                         copy_v3_v3(min, center);
2440                 }
2441         }
2442
2443         mid_v3_v3v3(t->center, min, max);
2444
2445         if (max[0] == min[0]) max[0] += 0.1f;  /* not optimal, but flipping is better than invalid garbage (i.e. division by zero!) */
2446         t->val = (max[0] - min[0]) / 2.0f; /* t->val is X dimension projected boundbox */
2447 }
2448
2449 int handleEventWarp(TransInfo *t, wmEvent *event)
2450 {
2451         int status = 0;
2452         
2453         if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
2454                 // Use customData pointer to signal warp direction
2455                 if (t->customData == NULL)
2456                         t->customData = (void *)1;
2457                 else
2458                         t->customData = NULL;
2459                 
2460                 status = 1;
2461         }
2462         
2463         return status;
2464 }
2465
2466 int Warp(TransInfo *t, const int UNUSED(mval[2]))
2467 {
2468         TransData *td = t->data;
2469         float vec[3], circumfac, dist, phi0, co, si, *curs, cursor[3], gcursor[3];
2470         int i;
2471         char str[50];
2472         
2473         curs = give_cursor(t->scene, t->view);
2474         /*
2475          * gcursor is the one used for helpline.
2476          * It has to be in the same space as the drawing loop
2477          * (that means it needs to be in the object's space when in edit mode and
2478          *  in global space in object mode)
2479          *
2480          * cursor is used for calculations.
2481          * It needs to be in view space, but we need to take object's offset
2482          * into account if in Edit mode.
2483          */
2484         copy_v3_v3(cursor, curs);
2485         copy_v3_v3(gcursor, cursor);
2486         if (t->flag & T_EDIT) {
2487                 sub_v3_v3(cursor, t->obedit->obmat[3]);
2488                 sub_v3_v3(gcursor, t->obedit->obmat[3]);
2489                 mul_m3_v3(t->data->smtx, gcursor);
2490         }
2491         mul_m4_v3(t->viewmat, cursor);
2492         sub_v3_v3(cursor, t->viewmat[3]);
2493         
2494         /* amount of radians for warp */
2495         circumfac = t->values[0];
2496         
2497         snapGrid(t, &circumfac);
2498         applyNumInput(&t->num, &circumfac);
2499         
2500         /* header print for NumInput */
2501         if (hasNumInput(&t->num)) {
2502                 char c[NUM_STR_REP_LEN];
2503                 
2504                 outputNumInput(&(t->num), c);
2505                 
2506                 sprintf(str, "Warp: %s", c);
2507
2508                 circumfac = DEG2RADF(circumfac);
2509         }
2510         else {
2511                 /* default header print */
2512                 sprintf(str, "Warp: %.3f", RAD2DEGF(circumfac));
2513         }
2514         
2515         t->values[0] = circumfac;
2516
2517         circumfac /= 2; /* only need 180 on each side to make 360 */
2518         
2519         for (i = 0; i < t->total; i++, td++) {
2520                 float loc[3];
2521                 if (td->flag & TD_NOACTION)
2522                         break;
2523                 
2524                 if (td->flag & TD_SKIP)
2525                         continue;
2526                 
2527                 /* translate point to center, rotate in such a way that outline==distance */
2528                 copy_v3_v3(vec, td->iloc);
2529                 mul_m3_v3(td->mtx, vec);
2530                 mul_m4_v3(t->viewmat, vec);
2531                 sub_v3_v3(vec, t->viewmat[3]);
2532                 
2533                 dist = vec[0] - cursor[0];
2534                 
2535                 /* t->val is X dimension projected boundbox */
2536                 phi0 = (circumfac * dist / t->val);
2537                 
2538                 vec[1] = (vec[1] - cursor[1]);
2539                 
2540                 co = (float)cos(phi0);
2541                 si = (float)sin(phi0);
2542                 loc[0] = -si * vec[1] + cursor[0];
2543                 loc[1] = co * vec[1] + cursor[1];
2544                 loc[2] = vec[2];
2545                 
2546                 mul_m4_v3(t->viewinv, loc);
2547                 sub_v3_v3(loc, t->viewinv[3]);
2548                 mul_m3_v3(td->smtx, loc);
2549                 
2550                 sub_v3_v3(loc, td->iloc);
2551                 mul_v3_fl(loc, td->factor);
2552                 add_v3_v3v3(td->loc, td->iloc, loc);
2553         }
2554         
2555         recalcData(t);
2556         
2557         ED_area_headerprint(t->sa, str);
2558         
2559         return 1;
2560 }
2561
2562 /* ************************** SHEAR *************************** */
2563
2564 static void postInputShear(TransInfo *UNUSED(t), float values[3])
2565 {
2566         mul_v3_fl(values, 0.05f);
2567 }
2568
2569 void initShear(TransInfo *t)
2570 {
2571         t->mode = TFM_SHEAR;
2572         t->transform = Shear;
2573         t->handleEvent = handleEventShear;
2574         
2575         setInputPostFct(&t->mouse, postInputShear);
2576         initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE);
2577         
2578         t->idx_max = 0;
2579         t->num.idx_max = 0;
2580         t->snap[0] = 0.0f;
2581         t->snap[1] = 0.1f;
2582         t->snap[2] = t->snap[1] * 0.1f;
2583         
2584         t->num.increment = 0.1f;
2585
2586         t->flag |= T_NO_CONSTRAINT;
2587 }
2588
2589 int handleEventShear(TransInfo *t, wmEvent *event)
2590 {
2591         int status = 0;
2592         
2593         if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
2594                 // Use customData pointer to signal Shear direction
2595                 if (t->customData == NULL) {
2596                         initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
2597                         t->customData = (void *)1;
2598                 }
2599                 else {
2600                         initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE);
2601                         t->customData = NULL;
2602                 }
2603
2604                 status = 1;
2605         }
2606         
2607         return status;
2608 }
2609
2610
2611 int Shear(TransInfo *t, const int UNUSED(mval[2]))
2612 {
2613         TransData *td = t->data;
2614         float vec[3];
2615         float smat[3][3], tmat[3][3], totmat[3][3], persmat[3][3], persinv[3][3];
2616         float value;
2617         int i;
2618         char str[50];
2619         
2620         copy_m3_m4(persmat, t->viewmat);
2621         invert_m3_m3(persinv, persmat);
2622         
2623         value = t->values[0];
2624         
2625         snapGrid(t, &value);
2626         
2627         applyNumInput(&t->num, &value);
2628         
2629         /* header print for NumInput */
2630         if (hasNumInput(&t->num)) {
2631                 char c[NUM_STR_REP_LEN];
2632                 
2633                 outputNumInput(&(t->num), c);
2634                 
2635                 sprintf(str, "Shear: %s %s", c, t->proptext);
2636         }
2637         else {
2638                 /* default header print */
2639                 sprintf(str, "Shear: %.3f %s", value, t->proptext);
2640         }
2641         
2642         t->values[0] = value;
2643
2644         unit_m3(smat);
2645         
2646         // Custom data signals shear direction
2647         if (t->customData == NULL)
2648                 smat[1][0] = value;
2649         else
2650                 smat[0][1] = value;
2651         
2652         mul_m3_m3m3(tmat, smat, persmat);
2653         mul_m3_m3m3(totmat, persinv, tmat);
2654         
2655         for (i = 0; i < t->total; i++, td++) {
2656                 if (td->flag & TD_NOACTION)
2657                         break;
2658                 
2659                 if (td->flag & TD_SKIP)
2660                         continue;
2661                 
2662                 if (t->obedit) {
2663                         float mat3[3][3];
2664                         mul_m3_m3m3(mat3, totmat, td->mtx);
2665                         mul_m3_m3m3(tmat, td->smtx, mat3);
2666                 }
2667                 else {
2668                         copy_m3_m3(tmat, totmat);
2669                 }
2670                 sub_v3_v3v3(vec, td->center, t->center);
2671                 
2672                 mul_m3_v3(tmat, vec);
2673                 
2674                 add_v3_v3(vec, t->center);
2675                 sub_v3_v3(vec, td->center);
2676                 
2677                 mul_v3_fl(vec, td->factor);
2678                 
2679                 add_v3_v3v3(td->loc, td->iloc, vec);
2680         }
2681         
2682         recalcData(t);
2683         
2684         ED_area_headerprint(t->sa, str);
2685
2686         return 1;
2687 }
2688
2689 /* ************************** RESIZE *************************** */
2690
2691 void initResize(TransInfo *t)
2692 {
2693         t->mode = TFM_RESIZE;
2694         t->transform = Resize;
2695         
2696         initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
2697         
2698         t->flag |= T_NULL_ONE;
2699         t->num.flag |= NUM_NULL_ONE;
2700         t->num.flag |= NUM_AFFECT_ALL;
2701         if (!t->obedit) {
2702                 t->flag |= T_NO_ZERO;
2703                 t->num.flag |= NUM_NO_ZERO;
2704         }
2705         
2706         t->idx_max = 2;
2707         t->num.idx_max = 2;
2708         t->snap[0] = 0.0f;
2709         t->snap[1] = 0.1f;
2710         t->snap[2] = t->snap[1] * 0.1f;
2711
2712         t->num.increment = t->snap[1];
2713 }
2714
2715 static void headerResize(TransInfo *t, float vec[3], char *str)
2716 {
2717         char tvec[NUM_STR_REP_LEN * 3];
2718         char *spos = str;
2719         if (hasNumInput(&t->num)) {
2720                 outputNumInput(&(t->num), tvec);
2721         }
2722         else {
2723                 BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", vec[0]);
2724                 BLI_snprintf(&tvec[NUM_STR_REP_LEN], NUM_STR_REP_LEN, "%.4f", vec[1]);
2725                 BLI_snprintf(&tvec[NUM_STR_REP_LEN * 2], NUM_STR_REP_LEN, "%.4f", vec[2]);
2726         }
2727         
2728         if (t->con.mode & CON_APPLY) {
2729                 switch (t->num.idx_max) {
2730                         case 0:
2731                                 spos += sprintf(spos, "Scale: %s%s %s", &tvec[0], t->con.text, t->proptext);
2732                                 break;
2733                         case 1:
2734                                 spos += sprintf(spos, "Scale: %s : %s%s %s", &tvec[0], &tvec[NUM_STR_REP_LEN],
2735                                                 t->con.text, t->proptext);
2736                                 break;
2737                         case 2:
2738                                 spos += sprintf(spos, "Scale: %s : %s : %s%s %s", &tvec[0], &tvec[NUM_STR_REP_LEN],
2739                                                 &tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext);
2740                 }
2741         }
2742         else {
2743                 if (t->flag & T_2D_EDIT) {
2744                         spos += sprintf(spos, "Scale X: %s   Y: %s%s %s", &tvec[0], &tvec[NUM_STR_REP_LEN],
2745                                         t->con.text, t->proptext);
2746                 }
2747                 else {
2748                         spos += sprintf(spos, "Scale X: %s   Y: %s  Z: %s%s %s", &tvec[0], &tvec[NUM_STR_REP_LEN],
2749                                         &tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext);
2750                 }
2751         }
2752         
2753         if (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
2754                 spos += sprintf(spos, " Proportional size: %.2f", t->prop_size);
2755         }
2756
2757         (void)spos;
2758 }
2759
2760 #define SIGN(a)     (a<-FLT_EPSILON ? 1 : a>FLT_EPSILON ? 2 : 3)
2761 #define VECSIGNFLIP(a, b) ((SIGN(a[0]) & SIGN(b[0])) == 0 || (SIGN(a[1]) & SIGN(b[1])) == 0 || (SIGN(a[2]) & SIGN(b[2])) == 0)
2762
2763 /* smat is reference matrix, only scaled */
2764 static void TransMat3ToSize(float mat[][3], float smat[][3], float *size)
2765 {
2766         float vec[3];
2767         
2768         copy_v3_v3(vec, mat[0]);
2769         size[0] = normalize_v3(vec);
2770         copy_v3_v3(vec, mat[1]);
2771         size[1] = normalize_v3(vec);
2772         copy_v3_v3(vec, mat[2]);
2773         size[2] = normalize_v3(vec);
2774         
2775         /* first tried with dotproduct... but the sign flip is crucial */
2776         if (VECSIGNFLIP(mat[0], smat[0]) ) size[0] = -size[0];
2777         if (VECSIGNFLIP(mat[1], smat[1]) ) size[1] = -size[1];
2778         if (VECSIGNFLIP(mat[2], smat[2]) ) size[2] = -size[2];
2779 }
2780
2781
2782 static void ElementResize(TransInfo *t, TransData *td, float mat[3][3])
2783 {
2784         float tmat[3][3], smat[3][3], center[3];
2785         float vec[3];
2786         
2787         if (t->flag & T_EDIT) {
2788                 mul_m3_m3m3(smat, mat, td->mtx);
2789                 mul_m3_m3m3(tmat, td->smtx, smat);
2790         }
2791         else {
2792                 copy_m3_m3(tmat, mat);
2793         }
2794         
2795         if (t->con.applySize) {
2796                 t->con.applySize(t, td, tmat);
2797         }
2798         
2799         /* local constraint shouldn't alter center */
2800         if ((t->around == V3D_LOCAL) &&
2801             (   (t->flag & (T_OBJECT | T_POSE)) ||
2802                 ((t->flag & T_EDIT) && (t->settings->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_FACE))) ||
2803                 (t->obedit && t->obedit->type == OB_ARMATURE))
2804             )
2805         {
2806                 copy_v3_v3(center, td->center);
2807         }
2808         else if (t->options & CTX_MOVIECLIP) {
2809                 copy_v3_v3(center, td->center);
2810         }
2811         else {
2812                 copy_v3_v3(center, t->center);
2813         }
2814
2815         if (td->ext) {
2816                 float fsize[3];
2817                 
2818                 if (t->flag & (T_OBJECT | T_TEXTURE | T_POSE)) {
2819                         float obsizemat[3][3];
2820                         // Reorient the size mat to fit the oriented object.
2821                         mul_m3_m3m3(obsizemat, tmat, td->axismtx);
2822                         //print_m3("obsizemat", obsizemat);
2823                         TransMat3ToSize(obsizemat, td->axismtx, fsize);
2824                         //print_v3("fsize", fsize);
2825                 }
2826                 else {
2827                         mat3_to_size(fsize, tmat);
2828                 }
2829                 
2830                 protectedSizeBits(td->protectflag, fsize);
2831                 
2832                 if ((t->flag & T_V3D_ALIGN) == 0) {   // align mode doesn't resize objects itself
2833                         if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2834                                 /* scale val and reset size */
2835                                 *td->val = td->ival * (1 + (fsize[0] - 1) * td->factor);
2836                                 
2837                                 td->ext->size[0] = td->ext->isize[0];
2838                                 td->ext->size[1] = td->ext->isize[1];
2839                                 td->ext->size[2] = td->ext->isize[2];
2840                         }
2841                         else {
2842                                 /* Reset val if SINGLESIZE but using a constraint */
2843                                 if (td->flag & TD_SINGLESIZE)
2844                                         *td->val = td->ival;
2845                                 
2846                                 td->ext->size[0] = td->ext->isize[0] * (1 + (fsize[0] - 1) * td->factor);
2847                                 td->ext->size[1] = td->ext->isize[1] * (1 + (fsize[1] - 1) * td->factor);
2848                                 td->ext->size[2] = td->ext->isize[2] * (1 + (fsize[2] - 1) * td->factor);
2849                         }
2850                 }
2851                 
2852                 constraintSizeLim(t, td);
2853         }
2854
2855         /* For individual element center, Editmode need to use iloc */
2856         if (t->flag & T_POINTS)
2857                 sub_v3_v3v3(vec, td->iloc, center);
2858         else
2859                 sub_v3_v3v3(vec, td->center, center);
2860         
2861         mul_m3_v3(tmat, vec);
2862         
2863         add_v3_v3(vec, center);
2864         if (t->flag & T_POINTS)
2865                 sub_v3_v3(vec, td->iloc);
2866         else
2867                 sub_v3_v3(vec, td->center);
2868         
2869         mul_v3_fl(vec, td->factor);
2870         
2871         if (t->flag & (T_OBJECT | T_POSE)) {
2872                 mul_m3_v3(td->smtx, vec);
2873         }
2874         
2875         protectedTransBits(td->protectflag, vec);
2876         add_v3_v3v3(td->loc, td->iloc, vec);
2877         
2878         constraintTransLim(t, td);
2879 }
2880
2881 int Resize(TransInfo *t, const int mval[2])
2882 {
2883         TransData *td;
2884         float size[3], mat[3][3];
2885         float ratio;
2886         int i;
2887         char str[200];
2888
2889         /* for manipulator, center handle, the scaling can't be done relative to center */
2890         if ((t->flag & T_USES_MANIPULATOR) && t->con.mode == 0) {
2891                 ratio = 1.0f - ((t->imval[0] - mval[0]) + (t->imval[1] - mval[1])) / 100.0f;
2892         }
2893         else {
2894                 ratio = t->values[0];
2895         }
2896         
2897         size[0] = size[1] = size[2] = ratio;
2898         
2899         snapGrid(t, size);
2900         
2901         if (hasNumInput(&t->num)) {
2902                 applyNumInput(&t->num, size);
2903                 constraintNumInput(t, size);
2904         }
2905         
2906         applySnapping(t, size);
2907         
2908         if (t->flag & T_AUTOVALUES) {
2909                 copy_v3_v3(size, t->auto_values);
2910         }
2911         
2912         copy_v3_v3(t->values, size);
2913         
2914         size_to_mat3(mat, size);
2915         
2916         if (t->con.applySize) {
2917                 t->con.applySize(t, NULL, mat);
2918         }
2919         
2920         copy_m3_m3(t->mat, mat);    // used in manipulator
2921         
2922         headerResize(t, size, str);
2923         
2924         for (i = 0, td = t->data; i < t->total; i++, td++) {
2925                 if (td->flag & TD_NOACTION)
2926                         break;