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