commit from r27250 to fix bug #21433, broke local axis rotation for pose bones and...
[blender.git] / source / blender / editors / transform / transform_constraints.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <math.h>
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #ifndef WIN32
40 #include <unistd.h>
41 #else
42 #include <io.h>
43 #endif
44
45 #include "MEM_guardedalloc.h"
46
47 #include "DNA_action_types.h"
48 #include "DNA_armature_types.h"
49 #include "DNA_camera_types.h"
50 #include "DNA_curve_types.h"
51 #include "DNA_effect_types.h"
52 #include "DNA_image_types.h"
53 #include "DNA_ipo_types.h"
54 #include "DNA_key_types.h"
55 #include "DNA_lamp_types.h"
56 #include "DNA_lattice_types.h"
57 #include "DNA_mesh_types.h"
58 #include "DNA_meshdata_types.h"
59 #include "DNA_meta_types.h"
60 #include "DNA_object_types.h"
61 #include "DNA_scene_types.h"
62 #include "DNA_screen_types.h"
63 #include "DNA_space_types.h"
64 #include "DNA_view3d_types.h"
65
66 #include "BIF_gl.h"
67 #include "BIF_glutil.h"
68
69 #include "BKE_context.h"
70 #include "BKE_global.h"
71 #include "BKE_utildefines.h"
72
73 #include "ED_image.h"
74 #include "ED_view3d.h"
75
76 #include "BLI_math.h"
77
78 //#include "blendef.h"
79 //
80 //#include "mydevice.h"
81
82 #include "WM_types.h"
83 #include "UI_resources.h"
84
85
86 #include "transform.h"
87
88 static void drawObjectConstraint(TransInfo *t);
89
90 /* ************************** CONSTRAINTS ************************* */
91 void constraintAutoValues(TransInfo *t, float vec[3])
92 {
93         int mode = t->con.mode;
94         if (mode & CON_APPLY)
95         {
96                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
97
98                 if ((mode & CON_AXIS0) == 0)
99                 {
100                         vec[0] = nval;
101                 }
102                 if ((mode & CON_AXIS1) == 0)
103                 {
104                         vec[1] = nval;
105                 }
106                 if ((mode & CON_AXIS2) == 0)
107                 {
108                         vec[2] = nval;
109                 }
110         }
111 }
112
113 void constraintNumInput(TransInfo *t, float vec[3])
114 {
115         int mode = t->con.mode;
116         if (mode & CON_APPLY) {
117                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
118
119                 if (getConstraintSpaceDimension(t) == 2) {
120                         int axis = mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
121                         if (axis == (CON_AXIS0|CON_AXIS1)) {
122                                 vec[0] = vec[0];
123                                 vec[1] = vec[1];
124                                 vec[2] = nval;
125                         }
126                         else if (axis == (CON_AXIS1|CON_AXIS2)) {
127                                 vec[2] = vec[1];
128                                 vec[1] = vec[0];
129                                 vec[0] = nval;
130                         }
131                         else if (axis == (CON_AXIS0|CON_AXIS2)) {
132                                 vec[0] = vec[0];
133                                 vec[2] = vec[1];
134                                 vec[1] = nval;
135                         }
136                 }
137                 else if (getConstraintSpaceDimension(t) == 1) {
138                         if (mode & CON_AXIS0) {
139                                 vec[0] = vec[0];
140                                 vec[1] = nval;
141                                 vec[2] = nval;
142                         }
143                         else if (mode & CON_AXIS1) {
144                                 vec[1] = vec[0];
145                                 vec[0] = nval;
146                                 vec[2] = nval;
147                         }
148                         else if (mode & CON_AXIS2) {
149                                 vec[2] = vec[0];
150                                 vec[0] = nval;
151                                 vec[1] = nval;
152                         }
153                 }
154         }
155 }
156
157 static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3]) {
158         int i = 0;
159
160         mul_m3_v3(t->con.imtx, vec);
161
162         snapGrid(t, vec);
163
164         if (t->num.flag & T_NULL_ONE) {
165                 if (!(t->con.mode & CON_AXIS0))
166                         vec[0] = 1.0f;
167
168                 if (!(t->con.mode & CON_AXIS1))
169                         vec[1] = 1.0f;
170
171                 if (!(t->con.mode & CON_AXIS2))
172                         vec[2] = 1.0f;
173         }
174
175         if (hasNumInput(&t->num)) {
176                 applyNumInput(&t->num, vec);
177                 constraintNumInput(t, vec);
178         }
179
180         /* autovalues is operator param, use that directly but not if snapping is forced */
181         if (t->flag & T_AUTOVALUES && (t->tsnap.status & SNAP_FORCED) == 0)
182         {
183                 VECCOPY(vec, t->auto_values);
184                 constraintAutoValues(t, vec);
185         }
186
187         if (t->con.mode & CON_AXIS0) {
188                 pvec[i++] = vec[0];
189         }
190         if (t->con.mode & CON_AXIS1) {
191                 pvec[i++] = vec[1];
192         }
193         if (t->con.mode & CON_AXIS2) {
194                 pvec[i++] = vec[2];
195         }
196
197         mul_m3_v3(t->con.mtx, vec);
198 }
199
200 static void axisProjection(TransInfo *t, float axis[3], float in[3], float out[3]) {
201         float norm[3], vec[3], factor, angle;
202
203         if(in[0]==0.0f && in[1]==0.0f && in[2]==0.0f)
204                 return;
205
206         angle = fabs(angle_v3v3(axis, t->viewinv[2]));
207         if (angle > M_PI / 2) {
208                 angle = M_PI - angle;
209         }
210         angle = 180.0f * angle / M_PI;
211
212         /* For when view is parallel to constraint... will cause NaNs otherwise
213            So we take vertical motion in 3D space and apply it to the
214            constraint axis. Nice for camera grab + MMB */
215         if(angle < 5.0f) {
216                 project_v3_v3v3(vec, in, t->viewinv[1]);
217                 factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
218                 /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
219                 if(factor<0.0f) factor*= -factor;
220                 else factor*= factor;
221
222                 VECCOPY(out, axis);
223                 normalize_v3(out);
224                 mul_v3_fl(out, -factor);        /* -factor makes move down going backwards */
225         }
226         else {
227                 float v[3], i1[3], i2[3];
228                 float v2[3], v4[3];
229                 float norm_center[3];
230                 float plane[3];
231
232                 getViewVector(t, t->con.center, norm_center);
233                 cross_v3_v3v3(plane, norm_center, axis);
234
235                 project_v3_v3v3(vec, in, plane);
236                 sub_v3_v3v3(vec, in, vec);
237                 
238                 add_v3_v3v3(v, vec, t->con.center);
239                 getViewVector(t, v, norm);
240
241                 /* give arbitrary large value if projection is impossible */
242                 factor = dot_v3v3(axis, norm);
243                 if (1 - fabs(factor) < 0.0002f) {
244                         VECCOPY(out, axis);
245                         if (factor > 0) {
246                                 mul_v3_fl(out, 1000000000);
247                         } else {
248                                 mul_v3_fl(out, -1000000000);
249                         }
250                 } else {
251                         add_v3_v3v3(v2, t->con.center, axis);
252                         add_v3_v3v3(v4, v, norm);
253                         
254                         isect_line_line_v3(t->con.center, v2, v, v4, i1, i2);
255                         
256                         sub_v3_v3v3(v, i2, v);
257         
258                         sub_v3_v3v3(out, i1, t->con.center);
259                 }
260         }
261 }
262
263 static void planeProjection(TransInfo *t, float in[3], float out[3]) {
264         float vec[3], factor, norm[3];
265
266         add_v3_v3v3(vec, in, t->con.center);
267         getViewVector(t, vec, norm);
268
269         sub_v3_v3v3(vec, out, in);
270
271         factor = dot_v3v3(vec, norm);
272         if (fabs(factor) <= 0.001) {
273                 return; /* prevent divide by zero */
274         }
275         factor = dot_v3v3(vec, vec) / factor;
276
277         VECCOPY(vec, norm);
278         mul_v3_fl(vec, factor);
279
280         add_v3_v3v3(out, in, vec);
281 }
282
283 /*
284  * Generic callback for constant spacial constraints applied to linear motion
285  *
286  * The IN vector in projected into the constrained space and then further
287  * projected along the view vector.
288  * (in perspective mode, the view vector is relative to the position on screen)
289  *
290  */
291
292 static void applyAxisConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
293 {
294         VECCOPY(out, in);
295         if (!td && t->con.mode & CON_APPLY) {
296                 mul_m3_v3(t->con.pmtx, out);
297
298                 // With snap, a projection is alright, no need to correct for view alignment
299                 if (!(t->tsnap.mode != SCE_SNAP_MODE_INCREMENT && activeSnap(t))) {
300                         if (getConstraintSpaceDimension(t) == 2) {
301                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
302                                         planeProjection(t, in, out);
303                                 }
304                         }
305                         else if (getConstraintSpaceDimension(t) == 1) {
306                                 float c[3];
307
308                                 if (t->con.mode & CON_AXIS0) {
309                                         VECCOPY(c, t->con.mtx[0]);
310                                 }
311                                 else if (t->con.mode & CON_AXIS1) {
312                                         VECCOPY(c, t->con.mtx[1]);
313                                 }
314                                 else if (t->con.mode & CON_AXIS2) {
315                                         VECCOPY(c, t->con.mtx[2]);
316                                 }
317                                 axisProjection(t, c, in, out);
318                         }
319                 }
320                 postConstraintChecks(t, out, pvec);
321         }
322 }
323
324 /*
325  * Generic callback for object based spatial constraints applied to linear motion
326  *
327  * At first, the following is applied to the first data in the array
328  * The IN vector in projected into the constrained space and then further
329  * projected along the view vector.
330  * (in perspective mode, the view vector is relative to the position on screen)
331  *
332  * Further down, that vector is mapped to each data's space.
333  */
334
335 static void applyObjectConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
336 {
337         VECCOPY(out, in);
338         if (t->con.mode & CON_APPLY) {
339                 if (!td) {
340                         mul_m3_v3(t->con.pmtx, out);
341                         if (getConstraintSpaceDimension(t) == 2) {
342                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
343                                         planeProjection(t, in, out);
344                                 }
345                         }
346                         else if (getConstraintSpaceDimension(t) == 1) {
347                                 float c[3];
348
349                                 if (t->con.mode & CON_AXIS0) {
350                                         VECCOPY(c, t->con.mtx[0]);
351                                 }
352                                 else if (t->con.mode & CON_AXIS1) {
353                                         VECCOPY(c, t->con.mtx[1]);
354                                 }
355                                 else if (t->con.mode & CON_AXIS2) {
356                                         VECCOPY(c, t->con.mtx[2]);
357                                 }
358                                 axisProjection(t, c, in, out);
359                         }
360                         postConstraintChecks(t, out, pvec);
361                         VECCOPY(out, pvec);
362                 }
363                 else {
364                         int i=0;
365
366                         out[0] = out[1] = out[2] = 0.0f;
367                         if (t->con.mode & CON_AXIS0) {
368                                 out[0] = in[i++];
369                         }
370                         if (t->con.mode & CON_AXIS1) {
371                                 out[1] = in[i++];
372                         }
373                         if (t->con.mode & CON_AXIS2) {
374                                 out[2] = in[i++];
375                         }
376                         mul_m3_v3(td->axismtx, out);
377                 }
378         }
379 }
380
381 /*
382  * Generic callback for constant spacial constraints applied to resize motion
383  *
384  *
385  */
386
387 static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
388 {
389         if (!td && t->con.mode & CON_APPLY) {
390                 float tmat[3][3];
391
392                 if (!(t->con.mode & CON_AXIS0)) {
393                         smat[0][0] = 1.0f;
394                 }
395                 if (!(t->con.mode & CON_AXIS1)) {
396                         smat[1][1] = 1.0f;
397                 }
398                 if (!(t->con.mode & CON_AXIS2)) {
399                         smat[2][2] = 1.0f;
400                 }
401
402                 mul_m3_m3m3(tmat, smat, t->con.imtx);
403                 mul_m3_m3m3(smat, t->con.mtx, tmat);
404         }
405 }
406
407 /*
408  * Callback for object based spacial constraints applied to resize motion
409  *
410  *
411  */
412
413 static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
414 {
415         if (td && t->con.mode & CON_APPLY) {
416                 float tmat[3][3];
417                 float imat[3][3];
418
419                 invert_m3_m3(imat, td->axismtx);
420
421                 if (!(t->con.mode & CON_AXIS0)) {
422                         smat[0][0] = 1.0f;
423                 }
424                 if (!(t->con.mode & CON_AXIS1)) {
425                         smat[1][1] = 1.0f;
426                 }
427                 if (!(t->con.mode & CON_AXIS2)) {
428                         smat[2][2] = 1.0f;
429                 }
430
431                 mul_m3_m3m3(tmat, smat, imat);
432                 mul_m3_m3m3(smat, td->axismtx, tmat);
433         }
434 }
435
436 /*
437  * Generic callback for constant spacial constraints applied to rotations
438  *
439  * The rotation axis is copied into VEC.
440  *
441  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
442  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
443  *
444  * The following only applies when CON_NOFLIP is not set.
445  * The vector is then modified to always point away from the screen (in global space)
446  * This insures that the rotation is always logically following the mouse.
447  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
448  */
449
450 static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle)
451 {
452         if (!td && t->con.mode & CON_APPLY) {
453                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
454
455                 switch(mode) {
456                 case CON_AXIS0:
457                 case (CON_AXIS1|CON_AXIS2):
458                         VECCOPY(vec, t->con.mtx[0]);
459                         break;
460                 case CON_AXIS1:
461                 case (CON_AXIS0|CON_AXIS2):
462                         VECCOPY(vec, t->con.mtx[1]);
463                         break;
464                 case CON_AXIS2:
465                 case (CON_AXIS0|CON_AXIS1):
466                         VECCOPY(vec, t->con.mtx[2]);
467                         break;
468                 }
469                 /* don't flip axis if asked to or if num input */
470                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
471                         if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
472                                 *angle = -(*angle);
473                         }
474                 }
475         }
476 }
477
478 /*
479  * Callback for object based spacial constraints applied to rotations
480  *
481  * The rotation axis is copied into VEC.
482  *
483  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
484  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
485  *
486  * The following only applies when CON_NOFLIP is not set.
487  * The vector is then modified to always point away from the screen (in global space)
488  * This insures that the rotation is always logically following the mouse.
489  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
490  */
491
492 static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle)
493 {
494         if (t->con.mode & CON_APPLY) {
495                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
496
497                 /* on setup call, use first object */
498                 if (td == NULL) {
499                         td= t->data;
500                 }
501
502                 switch(mode) {
503                 case CON_AXIS0:
504                 case (CON_AXIS1|CON_AXIS2):
505                         VECCOPY(vec, td->axismtx[0]);
506                         break;
507                 case CON_AXIS1:
508                 case (CON_AXIS0|CON_AXIS2):
509                         VECCOPY(vec, td->axismtx[1]);
510                         break;
511                 case CON_AXIS2:
512                 case (CON_AXIS0|CON_AXIS1):
513                         VECCOPY(vec, td->axismtx[2]);
514                         break;
515                 }
516                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
517                         if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
518                                 *angle = -(*angle);
519                         }
520                 }
521         }
522 }
523
524 /*--------------------- INTERNAL SETUP CALLS ------------------*/
525
526 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) {
527         strncpy(t->con.text + 1, text, 48);
528         copy_m3_m3(t->con.mtx, space);
529         t->con.mode = mode;
530         getConstraintMatrix(t);
531
532         startConstraint(t);
533
534         t->con.drawExtra = NULL;
535         t->con.applyVec = applyAxisConstraintVec;
536         t->con.applySize = applyAxisConstraintSize;
537         t->con.applyRot = applyAxisConstraintRot;
538         t->redraw = 1;
539 }
540
541 void setLocalConstraint(TransInfo *t, int mode, const char text[]) {
542         if (t->flag & T_EDIT) {
543                 float obmat[3][3];
544                 copy_m3_m4(obmat, t->scene->obedit->obmat);
545                 setConstraint(t, obmat, mode, text);
546         }
547         else {
548                 if (t->total == 1) {
549                         setConstraint(t, t->data->axismtx, mode, text);
550                 }
551                 else {
552                         strncpy(t->con.text + 1, text, 48);
553                         copy_m3_m3(t->con.mtx, t->data->axismtx);
554                         t->con.mode = mode;
555                         getConstraintMatrix(t);
556
557                         startConstraint(t);
558
559                         t->con.drawExtra = drawObjectConstraint;
560                         t->con.applyVec = applyObjectConstraintVec;
561                         t->con.applySize = applyObjectConstraintSize;
562                         t->con.applyRot = applyObjectConstraintRot;
563                         t->redraw = 1;
564                 }
565         }
566 }
567
568 /*
569         Set the constraint according to the user defined orientation
570
571         ftext is a format string passed to sprintf. It will add the name of
572         the orientation where %s is (logically).
573 */
574 void setUserConstraint(TransInfo *t, short orientation, int mode, const char ftext[]) {
575         char text[40];
576
577         switch(orientation) {
578         case V3D_MANIP_GLOBAL:
579                 {
580                         float mtx[3][3];
581                         sprintf(text, ftext, "global");
582                         unit_m3(mtx);
583                         setConstraint(t, mtx, mode, text);
584                 }
585                 break;
586         case V3D_MANIP_LOCAL:
587                 sprintf(text, ftext, "local");
588                 setLocalConstraint(t, mode, text);
589                 break;
590         case V3D_MANIP_NORMAL:
591                 sprintf(text, ftext, "normal");
592                 setConstraint(t, t->spacemtx, mode, text);
593                 break;
594         case V3D_MANIP_VIEW:
595                 sprintf(text, ftext, "view");
596                 setConstraint(t, t->spacemtx, mode, text);
597                 break;
598         case V3D_MANIP_GIMBAL:
599                 sprintf(text, ftext, "gimbal");
600                 setConstraint(t, t->spacemtx, mode, text);
601                 break;
602         default: /* V3D_MANIP_CUSTOM */
603                 sprintf(text, ftext, t->spacename);
604                 setConstraint(t, t->spacemtx, mode, text);
605                 break;
606         }
607
608         t->con.orientation = orientation;
609
610         t->con.mode |= CON_USER;
611 }
612
613 /*----------------- DRAWING CONSTRAINTS -------------------*/
614
615 void drawConstraint(const struct bContext *C, TransInfo *t)
616 {
617         TransCon *tc = &(t->con);
618
619         if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE))
620                 return;
621         if (!(tc->mode & CON_APPLY))
622                 return;
623         if (t->flag & T_USES_MANIPULATOR)
624                 return;
625         if (t->flag & T_NO_CONSTRAINT)
626                 return;
627
628         /* nasty exception for Z constraint in camera view */
629         // TRANSFORM_FIX_ME
630 //      if((t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp==V3D_CAMOB)
631 //              return;
632
633         if (tc->drawExtra) {
634                 tc->drawExtra(t);
635         }
636         else {
637                 if (tc->mode & CON_SELECT) {
638                         float vec[3];
639                         char col2[3] = {255,255,255};
640                         convertViewVec(t, vec, (short)(t->mval[0] - t->con.imval[0]), (short)(t->mval[1] - t->con.imval[1]));
641                         add_v3_v3v3(vec, vec, tc->center);
642
643                         drawLine(t, tc->center, tc->mtx[0], 'x', 0);
644                         drawLine(t, tc->center, tc->mtx[1], 'y', 0);
645                         drawLine(t, tc->center, tc->mtx[2], 'z', 0);
646
647                         glColor3ubv((GLubyte *)col2);
648
649                         glDisable(GL_DEPTH_TEST);
650                         setlinestyle(1);
651                         glBegin(GL_LINE_STRIP);
652                                 glVertex3fv(tc->center);
653                                 glVertex3fv(vec);
654                         glEnd();
655                         setlinestyle(0);
656                         // TRANSFORM_FIX_ME
657                         //if(G.vd->zbuf)
658                                 glEnable(GL_DEPTH_TEST);
659                 }
660
661                 if (tc->mode & CON_AXIS0) {
662                         drawLine(t, tc->center, tc->mtx[0], 'x', DRAWLIGHT);
663                 }
664                 if (tc->mode & CON_AXIS1) {
665                         drawLine(t, tc->center, tc->mtx[1], 'y', DRAWLIGHT);
666                 }
667                 if (tc->mode & CON_AXIS2) {
668                         drawLine(t, tc->center, tc->mtx[2], 'z', DRAWLIGHT);
669                 }
670         }
671 }
672
673 /* called from drawview.c, as an extra per-window draw option */
674 void drawPropCircle(const struct bContext *C, TransInfo *t)
675 {
676         if (t->flag & T_PROP_EDIT) {
677                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
678                 float tmat[4][4], imat[4][4];
679                 float center[3];
680
681                 UI_ThemeColor(TH_GRID);
682
683                 if(t->spacetype == SPACE_VIEW3D && rv3d != NULL)
684                 {
685                         copy_m4_m4(tmat, rv3d->viewmat);
686                         invert_m4_m4(imat, tmat);
687                 }
688                 else
689                 {
690                         unit_m4(tmat);
691                         unit_m4(imat);
692                 }
693
694                 glPushMatrix();
695
696                 VECCOPY(center, t->center);
697
698                 if((t->spacetype == SPACE_VIEW3D) && t->obedit)
699                 {
700                         mul_m4_v3(t->obedit->obmat, center); /* because t->center is in local space */
701                 }
702                 else if(t->spacetype == SPACE_IMAGE)
703                 {
704                         float aspx, aspy;
705
706                         ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
707                         glScalef(1.0f/aspx, 1.0f/aspy, 1.0);
708                 }
709
710                 set_inverted_drawing(1);
711                 drawcircball(GL_LINE_LOOP, center, t->prop_size, imat);
712                 set_inverted_drawing(0);
713
714                 glPopMatrix();
715         }
716 }
717
718 static void drawObjectConstraint(TransInfo *t) {
719         int i;
720         TransData * td = t->data;
721
722         /* Draw the first one lighter because that's the one who controls the others.
723            Meaning the transformation is projected on that one and just copied on the others
724            constraint space.
725            In a nutshell, the object with light axis is controlled by the user and the others follow.
726            Without drawing the first light, users have little clue what they are doing.
727          */
728         if (t->con.mode & CON_AXIS0) {
729                 drawLine(t, td->ob->obmat[3], td->axismtx[0], 'x', DRAWLIGHT);
730         }
731         if (t->con.mode & CON_AXIS1) {
732                 drawLine(t, td->ob->obmat[3], td->axismtx[1], 'y', DRAWLIGHT);
733         }
734         if (t->con.mode & CON_AXIS2) {
735                 drawLine(t, td->ob->obmat[3], td->axismtx[2], 'z', DRAWLIGHT);
736         }
737
738         td++;
739
740         for(i=1;i<t->total;i++,td++) {
741                 if (t->con.mode & CON_AXIS0) {
742                         drawLine(t, td->ob->obmat[3], td->axismtx[0], 'x', 0);
743                 }
744                 if (t->con.mode & CON_AXIS1) {
745                         drawLine(t, td->ob->obmat[3], td->axismtx[1], 'y', 0);
746                 }
747                 if (t->con.mode & CON_AXIS2) {
748                         drawLine(t, td->ob->obmat[3], td->axismtx[2], 'z', 0);
749                 }
750         }
751 }
752
753 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
754
755 void startConstraint(TransInfo *t) {
756         t->con.mode |= CON_APPLY;
757         *t->con.text = ' ';
758         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
759 }
760
761 void stopConstraint(TransInfo *t) {
762         t->con.mode &= ~(CON_APPLY|CON_SELECT);
763         *t->con.text = '\0';
764         t->num.idx_max = t->idx_max;
765 }
766
767 void getConstraintMatrix(TransInfo *t)
768 {
769         float mat[3][3];
770         invert_m3_m3(t->con.imtx, t->con.mtx);
771         unit_m3(t->con.pmtx);
772
773         if (!(t->con.mode & CON_AXIS0)) {
774                 t->con.pmtx[0][0]               =
775                         t->con.pmtx[0][1]       =
776                         t->con.pmtx[0][2]       = 0.0f;
777         }
778
779         if (!(t->con.mode & CON_AXIS1)) {
780                 t->con.pmtx[1][0]               =
781                         t->con.pmtx[1][1]       =
782                         t->con.pmtx[1][2]       = 0.0f;
783         }
784
785         if (!(t->con.mode & CON_AXIS2)) {
786                 t->con.pmtx[2][0]               =
787                         t->con.pmtx[2][1]       =
788                         t->con.pmtx[2][2]       = 0.0f;
789         }
790
791         mul_m3_m3m3(mat, t->con.pmtx, t->con.imtx);
792         mul_m3_m3m3(t->con.pmtx, t->con.mtx, mat);
793 }
794
795 /*------------------------- MMB Select -------------------------------*/
796
797 void initSelectConstraint(TransInfo *t, float mtx[3][3])
798 {
799         copy_m3_m3(t->con.mtx, mtx);
800         t->con.mode |= CON_APPLY;
801         t->con.mode |= CON_SELECT;
802
803         setNearestAxis(t);
804         t->con.drawExtra = NULL;
805         t->con.applyVec = applyAxisConstraintVec;
806         t->con.applySize = applyAxisConstraintSize;
807         t->con.applyRot = applyAxisConstraintRot;
808 }
809
810 void selectConstraint(TransInfo *t) {
811         if (t->con.mode & CON_SELECT) {
812                 setNearestAxis(t);
813                 startConstraint(t);
814         }
815 }
816
817 void postSelectConstraint(TransInfo *t)
818 {
819         if (!(t->con.mode & CON_SELECT))
820                 return;
821
822         t->con.mode &= ~CON_AXIS0;
823         t->con.mode &= ~CON_AXIS1;
824         t->con.mode &= ~CON_AXIS2;
825         t->con.mode &= ~CON_SELECT;
826
827         setNearestAxis(t);
828
829         startConstraint(t);
830         t->redraw = 1;
831 }
832
833 static void setNearestAxis2d(TransInfo *t)
834 {
835         /* no correction needed... just use whichever one is lower */
836         if ( abs(t->mval[0]-t->con.imval[0]) < abs(t->mval[1]-t->con.imval[1]) ) {
837                 t->con.mode |= CON_AXIS1;
838                 sprintf(t->con.text, " along Y axis");
839         }
840         else {
841                 t->con.mode |= CON_AXIS0;
842                 sprintf(t->con.text, " along X axis");
843         }
844 }
845
846 static void setNearestAxis3d(TransInfo *t)
847 {
848         float zfac;
849         float mvec[3], axis[3], proj[3];
850         float len[3];
851         int i, icoord[2];
852
853         /* calculate mouse movement */
854         mvec[0] = (float)(t->mval[0] - t->con.imval[0]);
855         mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
856         mvec[2] = 0.0f;
857
858         /* we need to correct axis length for the current zoomlevel of view,
859            this to prevent projected values to be clipped behind the camera
860            and to overflow the short integers.
861            The formula used is a bit stupid, just a simplification of the substraction
862            of two 2D points 30 pixels apart (that's the last factor in the formula) after
863            projecting them with window_to_3d_delta and then get the length of that vector.
864         */
865         zfac= t->persmat[0][3]*t->center[0]+ t->persmat[1][3]*t->center[1]+ t->persmat[2][3]*t->center[2]+ t->persmat[3][3];
866         zfac = len_v3(t->persinv[0]) * 2.0f/t->ar->winx * zfac * 30.0f;
867
868         for (i = 0; i<3; i++) {
869                 VECCOPY(axis, t->con.mtx[i]);
870
871                 mul_v3_fl(axis, zfac);
872                 /* now we can project to get window coordinate */
873                 add_v3_v3v3(axis, axis, t->con.center);
874                 projectIntView(t, axis, icoord);
875
876                 axis[0] = (float)(icoord[0] - t->center2d[0]);
877                 axis[1] = (float)(icoord[1] - t->center2d[1]);
878                 axis[2] = 0.0f;
879
880                 if (normalize_v3(axis) != 0.0f) {
881                         project_v3_v3v3(proj, mvec, axis);
882                         sub_v3_v3v3(axis, mvec, proj);
883                         len[i] = normalize_v3(axis);
884                 }
885                 else {
886                         len[i] = 10000000000.0f;
887                 }
888         }
889
890         if (len[0] <= len[1] && len[0] <= len[2]) {
891                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
892                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
893                         sprintf(t->con.text, " locking %s X axis", t->spacename);
894                 }
895                 else {
896                         t->con.mode |= CON_AXIS0;
897                         sprintf(t->con.text, " along %s X axis", t->spacename);
898                 }
899         }
900         else if (len[1] <= len[0] && len[1] <= len[2]) {
901                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
902                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
903                         sprintf(t->con.text, " locking %s Y axis", t->spacename);
904                 }
905                 else {
906                         t->con.mode |= CON_AXIS1;
907                         sprintf(t->con.text, " along %s Y axis", t->spacename);
908                 }
909         }
910         else if (len[2] <= len[1] && len[2] <= len[0]) {
911                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
912                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
913                         sprintf(t->con.text, " locking %s Z axis", t->spacename);
914                 }
915                 else {
916                         t->con.mode |= CON_AXIS2;
917                         sprintf(t->con.text, " along %s Z axis", t->spacename);
918                 }
919         }
920 }
921
922 void setNearestAxis(TransInfo *t)
923 {
924         /* clear any prior constraint flags */
925         t->con.mode &= ~CON_AXIS0;
926         t->con.mode &= ~CON_AXIS1;
927         t->con.mode &= ~CON_AXIS2;
928
929         /* constraint setting - depends on spacetype */
930         if (t->spacetype == SPACE_VIEW3D) {
931                 /* 3d-view */
932                 setNearestAxis3d(t);
933         }
934         else {
935                 /* assume that this means a 2D-Editor */
936                 setNearestAxis2d(t);
937         }
938
939         getConstraintMatrix(t);
940 }
941
942 /*-------------- HELPER FUNCTIONS ----------------*/
943
944 char constraintModeToChar(TransInfo *t) {
945         if ((t->con.mode & CON_APPLY)==0) {
946                 return '\0';
947         }
948         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
949         case (CON_AXIS0):
950         case (CON_AXIS1|CON_AXIS2):
951                 return 'X';
952         case (CON_AXIS1):
953         case (CON_AXIS0|CON_AXIS2):
954                 return 'Y';
955         case (CON_AXIS2):
956         case (CON_AXIS0|CON_AXIS1):
957                 return 'Z';
958         default:
959                 return '\0';
960         }
961 }
962
963
964 int isLockConstraint(TransInfo *t) {
965         int mode = t->con.mode;
966
967         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
968                 return 1;
969
970         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
971                 return 1;
972
973         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
974                 return 1;
975
976         return 0;
977 }
978
979 /*
980  * Returns the dimension of the constraint space.
981  *
982  * For that reason, the flags always needs to be set to properly evaluate here,
983  * even if they aren't actually used in the callback function. (Which could happen
984  * for weird constraints not yet designed. Along a path for example.)
985  */
986
987 int getConstraintSpaceDimension(TransInfo *t)
988 {
989         int n = 0;
990
991         if (t->con.mode & CON_AXIS0)
992                 n++;
993
994         if (t->con.mode & CON_AXIS1)
995                 n++;
996
997         if (t->con.mode & CON_AXIS2)
998                 n++;
999
1000         return n;
1001 /*
1002   Someone willing to do it criptically could do the following instead:
1003
1004   return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1005
1006   Based on the assumptions that the axis flags are one after the other and start at 1
1007 */
1008 }