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